Compare commits
21 Commits
master
...
onewire-sl
Author | SHA1 | Date |
---|---|---|
King Kévin | 9357342280 | |
King Kévin | f1e4624c2b | |
King Kévin | 73ac1db354 | |
King Kévin | f37b8309bb | |
King Kévin | 84a3da0449 | |
King Kévin | a1de197209 | |
King Kévin | 8a8424b349 | |
King Kévin | 5f4b03f6e1 | |
King Kévin | 3c340a9157 | |
King Kévin | 7fec0df419 | |
King Kévin | 8270f4e609 | |
King Kévin | a5e049d4fc | |
King Kévin | 17f92cd6c0 | |
King Kévin | fcded8a627 | |
King Kévin | d1b546350b | |
King Kévin | 51bda8b90f | |
King Kévin | 51dad418ad | |
King Kévin | 57f7e90cfd | |
King Kévin | b94c1d9fc3 | |
King Kévin | bab0602d96 | |
King Kévin | 6e71958c03 |
230
Makefile
230
Makefile
|
@ -1,230 +0,0 @@
|
|||
## This library is free software: you can redistribute it and/or modify
|
||||
## it under the terms of the GNU Lesser General Public License as published by
|
||||
## the Free Software Foundation, either version 3 of the License, or
|
||||
## (at your option) any later version.
|
||||
##
|
||||
## This library is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
## GNU Lesser General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU Lesser General Public License
|
||||
## along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
##
|
||||
|
||||
## the make file provide rule to compile and flash firmware for STM32F1 micro-controllers
|
||||
## it uses libopencm3
|
||||
|
||||
# be silent per default, but 'make V=1' will show all compiler calls.
|
||||
ifneq ($(V),1)
|
||||
Q := @
|
||||
NULL := 1> /dev/null 2> /dev/null
|
||||
endif
|
||||
|
||||
# main names (without extension, for input source file and output binary)
|
||||
APPLICATION = application
|
||||
BOOTLOADER = bootloader
|
||||
FIRMWARE = $(APPLICATION) $(BOOTLOADER)
|
||||
|
||||
# which development board is used
|
||||
# supported are: SYSTEM_BOARD, MAPLE_MINI, BLUE_PILL, CORE_BOARD
|
||||
BOARD = SYSTEM_BOARD
|
||||
|
||||
# opencm3 libraries
|
||||
OPENCM3_DIR := libopencm3
|
||||
OPENCM3_INC = $(OPENCM3_DIR)/include
|
||||
OPENCM3_LIB = $(OPENCM3_DIR)/lib
|
||||
|
||||
# library for the STM32F1 (provided by opencm3)
|
||||
STM32F1_LIB = opencm3_stm32f1
|
||||
# libraries with source code used (also to be compiled)
|
||||
SRCLIBS = . lib
|
||||
# source files (this will be populated using includes based DEPENDENCIES)
|
||||
CSRC =
|
||||
# headers (unique, and if existing) corresponding to source files
|
||||
CHDR = $(sort $(patsubst %.c,%.h,$(wildcard $(CSRC))))
|
||||
# objects (unique, and if existing) compiled from source files
|
||||
OBJ = $(sort $(patsubst %.c,%.o,$(wildcard $(CSRC))))
|
||||
# figure out based on the main sources files which library files are used
|
||||
DEPENDENCIES = $(patsubst %,%.inc,$(FIRMWARE))
|
||||
# populates CSRC based on the library files used
|
||||
-include $(DEPENDENCIES)
|
||||
|
||||
# executables for linking, compiling, debugging, ...
|
||||
# use ELLCC cross-compiling chain (based on clang/llvm + musl)
|
||||
ELLCC := /opt/ellcc/
|
||||
# use ELLCC as compile
|
||||
CC := $(ELLCC)bin/ecc -target arm-none-eabi
|
||||
LD := $(PREFIX)-ld
|
||||
LD := $(ELLCC)bin/ecc-ld -m armelf
|
||||
AR := $(ELLCC)bin/ecc-ar
|
||||
AS := $(ELLCC)bin/ecc-as
|
||||
OBJCOPY := $(ELLCC)bin/ecc-objcopy
|
||||
OBJDUMP := $(ELLCC)bin/ecc-objdump
|
||||
GDB := $(ELLCC)bin/ecc-gdb
|
||||
# ecc-gdb (0.13.3) is buggy (crash on kill, can't load elf)
|
||||
GDB := arm-none-eabi-gdb
|
||||
|
||||
# device micro-controller and board
|
||||
DEFS += -DSTM32F1 -D$(BOARD)
|
||||
|
||||
# C flags
|
||||
# optimize for size
|
||||
CFLAGS += -Os
|
||||
# add debug symbols (remove for smaller release)
|
||||
CFLAGS += -ggdb
|
||||
# use C99 (supported by most an sufficient)
|
||||
CFLAGS += -std=c99
|
||||
# have strict warning (for better code)
|
||||
CFLAGS += -Wpedantic -Wall -Werror -Wundef -Wextra -Wshadow -Wredundant-decls -Wmissing-prototypes -Wstrict-prototypes -Wstrict-overflow=5
|
||||
# add options for better code optimization
|
||||
CFLAGS += -fno-common -ffunction-sections -fdata-sections
|
||||
# use variable size enum (opencm3, gcc, and compiler-rt do)
|
||||
#CFLAGS += -fshort-enums
|
||||
# use no variable size enum (ELLCC/musl does not)
|
||||
CFLAGS += -fno-short-enums
|
||||
# don't use system main definition (the starting point)
|
||||
CFLAGS += -ffreestanding
|
||||
# don't use the standard library
|
||||
CFLAGS += -nostdlib -nostdinc
|
||||
# include ELLCC libraries
|
||||
CFLAGS += -I $(ELLCC)libecc/include/ -I $(ELLCC)libecc/include/arm/
|
||||
# include own libraries
|
||||
CFLAGS += $(foreach lib,$(SRCLIBS),-I $(lib))
|
||||
# include opencm3 libraries
|
||||
CFLAGS += -I $(OPENCM3_INC)
|
||||
# add defines for micro-controller and board
|
||||
CFLAGS += $(DEFS)
|
||||
|
||||
# linker flags
|
||||
# build static binary (no shared libraries on the micro-controller)
|
||||
LDFLAGS += -static
|
||||
# don's include the system start files
|
||||
LDFLAGS += -nostartfiles
|
||||
# only keep used sections
|
||||
LDFLAGS += --gc-sections
|
||||
# don't use system libraries
|
||||
LDFLAGS += -nostdlib -nostdinc
|
||||
# add ELLCC standard libraries (for libc, libm, libgcc)
|
||||
LDFLAGS += --library-path $(ELLCC)libecc/lib/cortex-m3-linux/
|
||||
# opencm3 libraries
|
||||
LDFLAGS += --library-path $(OPENCM3_LIB)
|
||||
# used libraries (gcc provides the ARM ABI, not sure how to replace with compiler-rt)
|
||||
LDLIBS += --library $(STM32F1_LIB) --library c --library m --library gcc
|
||||
|
||||
# target micro-controller information (ARM Cortex-M3 supports thumb and thumnb2, but does not include a floating point unit)
|
||||
ARCH_FLAGS = -mthumb -mcpu=cortex-m3 -msoft-float
|
||||
|
||||
# SWD adapter used
|
||||
# supported are : st-link v2 (STLINKV2), black magic probe (BMP)
|
||||
SWD_ADAPTER ?= BMP
|
||||
ifeq ($(SWD_ADAPTER),STLINKV2)
|
||||
# OpenOCD configuration
|
||||
OOCD ?= openocd
|
||||
OOCD_INTERFACE ?= stlink-v2
|
||||
OOCD_TARGET ?= stm32f1x
|
||||
else ifeq ($(SWD_ADAPTER),BMP)
|
||||
# the black magic probe has a SWD controller built in
|
||||
BMP_PORT ?= /dev/ttyACM0
|
||||
endif
|
||||
|
||||
# compile target rules
|
||||
all: elf hex bin
|
||||
|
||||
elf: $(patsubst %,%.elf,$(FIRMWARE))
|
||||
bin: $(patsubst %,%.bin,$(FIRMWARE))
|
||||
hex: $(patsubst %,%.hex,$(FIRMWARE))
|
||||
|
||||
%.hex: %.elf
|
||||
$(Q)$(OBJCOPY) --strip-all --strip-debug --output-target ihex $(<) $(@)
|
||||
|
||||
%.bin: %.elf
|
||||
$(Q)$(OBJCOPY) --strip-all --strip-debug --output-target binary $(<) $(@)
|
||||
|
||||
%.map %.list: %.elf
|
||||
$(Q)$(OBJDUMP) -S $(<) > $(@)
|
||||
|
||||
%.elf: %.o %.ld $(OBJ) $(OPENCM3_LIB)/lib$(STM32F1_LIB).a
|
||||
$(info linking $(@))
|
||||
$(Q)$(LD) $(LDFLAGS) --script $(*).ld $(<) $(OBJ) $(LDLIBS) -o $(@)
|
||||
$(Q)size $(@)
|
||||
|
||||
%.o: %.c $(CHDR) $(OPENCM3_LIB)/lib$(STM32F1_LIB).a
|
||||
$(info compiling $(@))
|
||||
$(Q)$(CC) $(CFLAGS) $(ARCH_FLAGS) -o $(@) -c $(<)
|
||||
|
||||
# find out which library source files also need to be compiled and linked, based on its dependencies
|
||||
%.inc: %.c $(OPENCM3_LIB)/lib$(STM32F1_LIB).a
|
||||
$(Q)echo "" > $(@)
|
||||
$(Q)for dependency in $(shell $(CC) $(CFLAGS) $(ARCH_FLAGS) -MM -c $(<)); do \
|
||||
if [ -f "$${dependency}" ]; then \
|
||||
for lib in $(SRCLIBS); do \
|
||||
if [ `dirname "$${dependency}"` = "$${lib}" ]; then \
|
||||
if [ -f `echo "$${dependency}" | sed -e 's|\(.*\)\.h$$|\1.c|'` ]; then \
|
||||
echo "$${dependency}" | sed -e 's|\(.*\)\.h$$|CSRC += \1.c\n-include \1.inc|g' -e 's|.*${*}.*||g' >> $(@); \
|
||||
fi; \
|
||||
fi; \
|
||||
done; \
|
||||
fi; \
|
||||
done
|
||||
|
||||
# get libopencm3
|
||||
$(OPENCM3_DIR)/Makefile:
|
||||
$(info checking out libopencm3 submodule)
|
||||
$(Q)git submodule init
|
||||
$(Q)git submodule update
|
||||
|
||||
# compile libopencm3
|
||||
$(OPENCM3_LIB)/lib$(STM32F1_LIB).a: $(OPENCM3_DIR)/Makefile
|
||||
$(info compiling libopencm3 submodule)
|
||||
$(Q)CFLAGS=-fno-short-enums $(MAKE) --directory $(OPENCM3_DIR)
|
||||
|
||||
# doxygen documentation
|
||||
doc: Doxyfile README.md $(patsubst %,%.c,$(FIRMWARE)) $(CSRC) $(CHDR)
|
||||
$(Q)doxygen $(<)
|
||||
|
||||
clean:
|
||||
$(Q)$(RM) $(BOOTLOADER).elf $(BOOTLOADER).bin $(BOOTLOADER).hex $(APPLICATION).elf $(APPLICATION).bin $(APPLICATION).hex $(OBJ) $(DEPENDENCIES) *.inc lib/*.inc
|
||||
|
||||
# flash application using DFU
|
||||
flash: $(APPLICATION).bin
|
||||
$(Q)dfu-util -d c440:0d00 -D $(<)
|
||||
|
||||
# flash bootloader using SWD
|
||||
flash_bootloader: $(BOOTLOADER).hex
|
||||
$(info flashing $(<) using SWD)
|
||||
ifeq ($(SWD_ADAPTER),STLINKV2)
|
||||
$(Q)$(OOCD) --file interface/$(OOCD_INTERFACE).cfg --file target/$(OOCD_TARGET).cfg --command "init" --command "reset init" --command "flash write_image erase $(<)" --command "reset" --command "shutdown" $(NULL)
|
||||
else ifeq ($(SWD_ADAPTER),BMP)
|
||||
$(Q)$(GDB) --eval-command="target extended-remote $(BMP_PORT)" --eval-command="set confirm off" --eval-command="monitor swdp_scan" --eval-command="attach 1" --eval-command="load" --eval-command="detach" --eval-command="quit" $(<)
|
||||
endif
|
||||
|
||||
# flash application using SWD
|
||||
flash_application: $(APPLICATION).hex
|
||||
$(info flashing $(<) using SWD)
|
||||
ifeq ($(SWD_ADAPTER),STLINKV2)
|
||||
$(Q)$(OOCD) --file interface/$(OOCD_INTERFACE).cfg --file target/$(OOCD_TARGET).cfg --command "init" --command "reset init" --command "flash write_image erase $(<)" --command "reset" --command "shutdown" $(NULL)
|
||||
else ifeq ($(SWD_ADAPTER),BMP)
|
||||
$(Q)$(GDB) --eval-command="target extended-remote $(BMP_PORT)" --eval-command="set confirm off" --eval-command="monitor swdp_scan" --eval-command="attach 1" --eval-command="load" --eval-command="kill" --eval-command="quit" $(<)
|
||||
endif
|
||||
|
||||
# reset device by setting the data width to 5 bis on the USB CDC ACM port
|
||||
ifeq ($(SWD_ADAPTER)$(BMP_PORT),BMP/dev/ttyACM0)
|
||||
ACM_PORT := /dev/ttyACM2
|
||||
else
|
||||
ACM_PORT := /dev/ttyACM0
|
||||
endif
|
||||
reset:
|
||||
$(Q)stty --file $(ACM_PORT) raw cs5
|
||||
$(Q)sleep 0.5
|
||||
|
||||
# debug application using GDB
|
||||
debug: $(APPLICATION).elf
|
||||
ifeq ($(SWD_ADAPTER),STLINKV2)
|
||||
# for GDB to work with openOCD the firmware needs to be reloaded
|
||||
$(Q)$(GDB) --eval-command="target remote | $(OOCD) --file interface/$(OOCD_INTERFACE).cfg --file target/$(OOCD_TARGET).cfg --command \"gdb_port pipe; log_output /dev/null; init\"" --eval-command="monitor reset halt" --eval-command="load" --eval-command="monitor reset init" $(<)
|
||||
else ifeq ($(SWD_ADAPTER),BMP)
|
||||
$(Q)$(GDB) --eval-command="target extended-remote $(BMP_PORT)" --eval-command="monitor version" --eval-command="monitor swdp_scan" --eval-command="attach 1" $(<)
|
||||
endif
|
||||
|
||||
.PHONY: clean elf bin hex srec list flash reset
|
33
README.md
33
README.md
|
@ -1,4 +1,4 @@
|
|||
This firmware template is designed for development boards based around [STM32 F1 series micro-controller](http://www.st.com/web/en/catalog/mmc/FM141/SC1169/SS1031).
|
||||
This firmware implements the Maxim DS2432 1k-Bit Protected 1-Wire EEPROM with SHA-1 Engine using a development board based around [STM32 F1 series micro-controller](http://www.st.com/web/en/catalog/mmc/FM141/SC1169/SS1031).
|
||||
|
||||
project
|
||||
=======
|
||||
|
@ -6,12 +6,20 @@ project
|
|||
summary
|
||||
-------
|
||||
|
||||
*describe project purpose*
|
||||
Maxim DS2432 1k-Bit Protected 1-Wire EEPROM with SHA-1 Engine based on the datasheet
|
||||
|
||||
technology
|
||||
----------
|
||||
|
||||
*described electronic details*
|
||||
This is a example application using the 1-Wire slave library
|
||||
|
||||
Following DS2432 features are not implemented:
|
||||
|
||||
- PF flag
|
||||
- Load First Secret function command
|
||||
- Compute Next Secret function command
|
||||
- Copy Scratchpad function command
|
||||
- prevent reading secret memory by returning 0xaa or 0x55
|
||||
|
||||
board
|
||||
=====
|
||||
|
@ -45,17 +53,18 @@ dependencies
|
|||
|
||||
The source code uses the [libopencm3](http://libopencm3.org/) library.
|
||||
The projects is already a git submodules.
|
||||
To initialize and it you just need to run once: `git submodule init` and `git submodule update`.
|
||||
It will be initialized when compiling the firmware.
|
||||
Alternatively you can run once: `git submodule init` and `git submodule update`.
|
||||
|
||||
firmware
|
||||
--------
|
||||
|
||||
To compile the firmware run `make`.
|
||||
To compile the firmware run `rake`.
|
||||
|
||||
documentation
|
||||
-------------
|
||||
|
||||
To generate doxygen documentation run `make doc`.
|
||||
To generate doxygen documentation run `rake doc`.
|
||||
|
||||
flash
|
||||
-----
|
||||
|
@ -63,23 +72,23 @@ flash
|
|||
There are two firmware images: `bootloader` and `application`.
|
||||
The `bootloader` image allows to flash the `application` over USB using the DFU protocol.
|
||||
The `bootloader` is started first and immediately jumps to the `application` if it is valid and the DFU mode is not forced (i.e. by pressing the user button on the board or requesting a DFU detach in the `application`).
|
||||
The main application should be implemented in `application.c`.
|
||||
The `application` image is the main application and is implemented in `application.c`.
|
||||
It is up to the application to advertise USB DFU support (i.e. as does the provided USB CDC ACM example).
|
||||
|
||||
The `bootlaoder` image will be flashed using SWD (Serial Wire Debug).
|
||||
For that you need an SWD adapter.
|
||||
The `Makefile` uses a Black Magic Probe (per default), or a ST-Link V2 along OpenOCD software.
|
||||
To flash the `booltoader` using SWD run `make flash_booloader`.
|
||||
To flash the `booltoader` using SWD run `rake flash_booloader`.
|
||||
|
||||
Once the `bootloader` flashed it is possible to flash the `application` over USB using the DFU protocol by running `make flash`.
|
||||
Once the `bootloader` is flashed it is possible to flash the `application` over USB using the DFU protocol by running `rake flash`.
|
||||
To force the bootloader to start the DFU mode press the user button or short a pin, depending on the board.
|
||||
It is also possible to flash the `application` using SWD by running `make flash_application`.
|
||||
It is also possible to flash the `application` image using SWD by running `rake flash_application`.
|
||||
|
||||
debug
|
||||
-----
|
||||
|
||||
SWD also allows to debug the code running on the micro-controller using GDB.
|
||||
To start the debugging session run `make debug`.
|
||||
To start the debugging session run `rake debug`.
|
||||
|
||||
USB
|
||||
---
|
||||
|
@ -87,5 +96,5 @@ USB
|
|||
The firmware offers serial communication over USART1 and USB (using the CDC ACM device class).
|
||||
|
||||
You can also reset the board by setting the serial width to 5 bits over USB.
|
||||
To reset the board run `make reset`.
|
||||
To reset the board run `rake reset`.
|
||||
This only works if provided USB CDC ACM is running correctly and the micro-controller isn't stuck.
|
||||
|
|
|
@ -0,0 +1,242 @@
|
|||
# encoding: utf-8
|
||||
# ruby: 2.4.2
|
||||
=begin
|
||||
Rakefile to manage compile CuVoodoo STM32F1 firmware.
|
||||
the firmware is for development board based around a STM32F1xx micro-controller.
|
||||
the firmware uses the libopencm3 library providing support for this micro-controller.
|
||||
=end
|
||||
require 'rake'
|
||||
require 'rake/clean'
|
||||
|
||||
# the firmwares to compile
|
||||
BOOTLOADER = "bootloader"
|
||||
APPLICATION = "application"
|
||||
FIRMWARES = [BOOTLOADER, APPLICATION]
|
||||
|
||||
# which development board is used
|
||||
# supported are: SYSTEM_BOARD, MAPLE_MINI, BLUE_PILL, CORE_BOARD
|
||||
BOARD = ENV["BOARD"] || "CORE_BOARD"
|
||||
|
||||
# libopencm3 definitions
|
||||
LIBOPENCM3_DIR = "libopencm3"
|
||||
LIBOPENCM3_INC = LIBOPENCM3_DIR+"/include"
|
||||
LIBOPENCM3_LIB = LIBOPENCM3_DIR+"/lib"
|
||||
# STM32F1 library use for this project provided libtm
|
||||
STM32F1_LIB = "opencm3_stm32f1"
|
||||
|
||||
# source code used by the firmware
|
||||
SRC_DIRS = [".", "lib"]
|
||||
|
||||
# cross-compiler environment
|
||||
PREFIX = ENV["PREFIX"] || "arm-none-eabi"
|
||||
CC = "clang -target #{PREFIX}" # use clang instead of gcc
|
||||
LD = PREFIX+"-ld"
|
||||
AR = PREFIX+"-ar"
|
||||
AS = PREFIX+"-as"
|
||||
OBJCOPY = PREFIX+"-objcopy"
|
||||
OBJDUMP = PREFIX+"-objdump"
|
||||
GDB = PREFIX+"-gdb"
|
||||
|
||||
# compiler flags
|
||||
cflags = [ENV["CFLAGS"]]
|
||||
# optimize for size
|
||||
cflags << "-Os"
|
||||
# add debug symbols (remove for smaller release)
|
||||
cflags << "-ggdb"
|
||||
# use C99 (supported by most an sufficient)
|
||||
cflags << "-std=c99"
|
||||
# have strict warning (for better code)
|
||||
cflags << "-Wpedantic -Wall -Werror -Wundef -Wextra -Wshadow -Wredundant-decls -Wmissing-prototypes -Wstrict-prototypes -Wstrict-overflow=5"
|
||||
# add options for better code optimization
|
||||
cflags << "-fno-common -ffunction-sections -fdata-sections"
|
||||
# use variable size enum (clang doesn't but opencm3, and gcc do)
|
||||
cflags << "-fshort-enums"
|
||||
# don't use system main definition (the starting point)
|
||||
cflags << "-ffreestanding"
|
||||
# don't use the standard library (only if you provide an alternative libc library)
|
||||
#cflags << "-nostdlib -nostdinc"
|
||||
# include own libraries
|
||||
cflags += SRC_DIRS.collect {|srd_dir| "-I #{srd_dir}"}
|
||||
# include libopencm3 library
|
||||
cflags << "-I #{LIBOPENCM3_INC}"
|
||||
# add defines for micro-controller and board
|
||||
cflags << "-DSTM32F1 -D#{BOARD}"
|
||||
# render cflags
|
||||
cflags = cflags.compact*' '
|
||||
|
||||
# linker flags
|
||||
ldflags = [ENV["LDFLAGS"]]
|
||||
# build static binary (no shared libraries on the micro-controller)
|
||||
ldflags << "-static"
|
||||
# don't include the system start files
|
||||
ldflags << "-nostartfiles"
|
||||
# only keep used sections
|
||||
ldflags << "--gc-sections"
|
||||
# don't use system libraries (only if you provide an alternative libc library)
|
||||
#ldflags << "-nostdlib -nostdinc"
|
||||
# add standard libraries (for libc, libm, libnosys, libgcc) and libopencm3
|
||||
library_paths = ["/usr/arm-none-eabi/lib/armv7-m/", "/usr/lib/gcc/arm-none-eabi/*/armv7-m/", LIBOPENCM3_LIB]
|
||||
ldflags += library_paths.collect {|library_path| "--library-path #{library_path}"}
|
||||
ldflags *= ' '
|
||||
# used libraries (gcc provides the ARM ABI)
|
||||
ldlibs = [STM32F1_LIB, "c", "m", "nosys", "gcc"]
|
||||
ldlibs = ldlibs.collect {|library| "--library #{library}"}
|
||||
ldlibs *= ' '
|
||||
|
||||
# target micro-controller information (ARM Cortex-M3 supports thumb and thumb2, but does not include a floating point unit)
|
||||
archflags = "-mthumb -mcpu=cortex-m3 -msoft-float"
|
||||
|
||||
desc "compile firmwares"
|
||||
task :default => FIRMWARES
|
||||
|
||||
FIRMWARES.each do |firmware|
|
||||
desc "compile #{firmware} firmware"
|
||||
task firmware => [firmware+".elf", firmware+".bin", firmware+".hex"]
|
||||
CLOBBER.include(firmware+".elf")
|
||||
CLOBBER.include(firmware+".bin")
|
||||
CLOBBER.include(firmware+".hex")
|
||||
CLOBBER.include(firmware+".list")
|
||||
CLOBBER.include(firmware+".map")
|
||||
end
|
||||
|
||||
# get dependencies of a file
|
||||
# done is a list of already known dependencies
|
||||
def dependencies(source, done=[])
|
||||
d_path = source.ext("d") # get the dependency file
|
||||
Rake::Task[d_path].invoke # ensure the dependency file exists
|
||||
d_file = IO.read(d_path) # read the dependencies from dependency file
|
||||
d_file = d_file.split(': ')[1].gsub("\n",'').gsub('\\ ','').gsub(/\s+/,' ').split(' ') # get a list of dependencies
|
||||
d_list = [] # list of dependencies
|
||||
# only save dependencies which are in our source directories
|
||||
d_file.each do |d|
|
||||
SRC_DIRS.each do |dir|
|
||||
if File.dirname(d)==dir then
|
||||
d_list << d
|
||||
end
|
||||
end
|
||||
end
|
||||
# get the dependencies of these dependencies, if we don't know them already
|
||||
done << source.ext("o")
|
||||
done.uniq!
|
||||
d_list.each do |d|
|
||||
d = d.ext("o")
|
||||
next if done.include? d
|
||||
done += dependencies(d, done)
|
||||
end
|
||||
done.uniq!
|
||||
return done
|
||||
end
|
||||
|
||||
desc "get libopencm3"
|
||||
file LIBOPENCM3_DIR+"/Makefile" do
|
||||
sh "git submodule init"
|
||||
sh "git submodule update"
|
||||
end
|
||||
|
||||
desc "compile libopencm3"
|
||||
file "#{LIBOPENCM3_LIB}/lib#{STM32F1_LIB}.a" => LIBOPENCM3_DIR+"/Makefile" do
|
||||
sh "make --directory #{LIBOPENCM3_DIR}"
|
||||
end
|
||||
|
||||
task :doc => ["Doxyfile", "README.md"] do |t|
|
||||
sh "doxygen #{t.source}"
|
||||
end
|
||||
|
||||
desc "compile source into object"
|
||||
rule '.o' => '.c' do |t|
|
||||
sh "#{CC} #{cflags} #{archflags} -o #{t.name} -c #{t.source}"
|
||||
end
|
||||
|
||||
desc "generate dependencies"
|
||||
rule '.d' => '.c' do |t|
|
||||
sh "#{CC} #{cflags} #{archflags} -MM -MF #{t.name} -c #{t.source}"
|
||||
end
|
||||
|
||||
desc "link binary"
|
||||
rule '.elf' => [proc{|f| dependencies(f)}, '.ld', "#{LIBOPENCM3_LIB}/lib#{STM32F1_LIB}.a"] do |t|
|
||||
sh "#{LD} #{ldflags} --script #{t.name.ext('ld')} #{t.prerequisites[0..-3].join(' ')} #{ldlibs} -o #{t.name}"
|
||||
sh "size #{t.name}"
|
||||
end
|
||||
|
||||
desc "export binary"
|
||||
rule '.bin' => '.elf' do |t|
|
||||
sh "#{OBJCOPY} --strip-all --strip-debug --output-target binary #{t.source} #{t.name}"
|
||||
end
|
||||
|
||||
desc "export intel hex file"
|
||||
rule '.hex' => '.elf' do |t|
|
||||
sh "#{OBJCOPY} --strip-all --strip-debug --output-target ihex #{t.source} #{t.name}"
|
||||
end
|
||||
|
||||
desc "export list"
|
||||
rule '.list' => '.elf' do |t|
|
||||
sh "#{OBJDUMP} -S #{t.source} > #{t.name}"
|
||||
end
|
||||
|
||||
desc "export map"
|
||||
rule '.map' => '.elf' do |t|
|
||||
sh "#{OBJDUMP} -S #{t.source} > #{t.name}"
|
||||
end
|
||||
|
||||
SRC_DIRS.each do |src_dir|
|
||||
CLEAN.include(src_dir+"/*.o")
|
||||
CLEAN.include(src_dir+"/*.d")
|
||||
end
|
||||
|
||||
# SWD/JTAG adapter used
|
||||
# supported are : STLINKV2 (ST-Link V2), BMP (Black Magic Probe)
|
||||
SWD_ADAPTER = ENV["SWD_ADAPTER"] || "BMP"
|
||||
# openOCD path to control the adapter
|
||||
OOCD = ENV["OOCD"] || "openocd"
|
||||
# openOCD adapted name
|
||||
OOCD_INTERFACE = ENV["OOCD_INTERFACE"] || (SWD_ADAPTER=="STLINKV2" ? "stlink-v2" : "")
|
||||
# openOCD target for the micro-controller
|
||||
OOCD_TARGET = "stm32f1x"
|
||||
# Black Magic Probe port
|
||||
BMP_PORT = ENV["BMP_PORT"] || "/dev/ttyACM0"
|
||||
|
||||
desc "flash application using USB DFU"
|
||||
task :flash => APPLICATION+".bin" do |t|
|
||||
sh "dfu-util -d c440:0d00 -D #{t.source}"
|
||||
end
|
||||
|
||||
desc "flash bootloader using SWD"
|
||||
task :flash_bootloader => BOOTLOADER+".hex" do |t|
|
||||
case SWD_ADAPTER
|
||||
when "STLINKV2"
|
||||
sh "#{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --file target/#{OOCD_TARGET}.cfg --command 'init' --command 'reset init' --command 'flash write_image erase #{t.source}' --command 'reset' --command 'shutdown'"
|
||||
when "BMP"
|
||||
sh "#{GDB} --eval-command='target extended-remote #{BMP_PORT}' --eval-command='set confirm off' --eval-command='monitor swdp_scan' --eval-command='attach 1' --eval-command='load' --eval-command='kill' --eval-command='quit' #{t.source}"
|
||||
end
|
||||
end
|
||||
|
||||
desc "flash application using SWD"
|
||||
task :flash_application => APPLICATION+".hex" do |t|
|
||||
case SWD_ADAPTER
|
||||
when "STLINKV2"
|
||||
sh "#{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --file target/#{OOCD_TARGET}.cfg --command 'init' --command 'reset init' --command 'flash write_image erase #{t.source}' --command 'reset' --command 'shutdown'"
|
||||
when "BMP"
|
||||
sh "#{GDB} --eval-command='target extended-remote #{BMP_PORT}' --eval-command='set confirm off' --eval-command='monitor swdp_scan' --eval-command='attach 1' --eval-command='load' --eval-command='kill' --eval-command='quit' #{t.source}"
|
||||
end
|
||||
end
|
||||
|
||||
# debug application using GDB
|
||||
desc "debug application using GDB"
|
||||
task :debug => APPLICATION+".elf" do |t|
|
||||
case SWD_ADAPTER
|
||||
when "STLINKV2"
|
||||
# for GDB to work with openOCD the firmware needs to be reloaded
|
||||
sh "#{GDB} --eval-command='target remote | #{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --file target/#{OOCD_TARGET}.cfg --command \"gdb_port pipe; log_output /dev/null; init\"' --eval-command='monitor reset halt' --eval-command='load' --eval-command='monitor reset init' #{t.source}"
|
||||
when "BMP"
|
||||
sh "#{GDB} --eval-command='target extended-remote #{BMP_PORT}' --eval-command='monitor version' --eval-command='monitor swdp_scan' --eval-command='attach 1' #{t.source}"
|
||||
end
|
||||
end
|
||||
|
||||
# reset device by setting the data width to 5 bis on the USB CDC ACM port
|
||||
ACM_PORT = ENV["ACM_PORT"] || ("/dev/ttyACM0"==BMP_PORT ? "/dev/ttyACM2" : "/dev/ttyACM0")
|
||||
|
||||
desc "reset application using serial"
|
||||
task :reset do
|
||||
sh "stty --file #{ACM_PORT} raw cs5"
|
||||
sleep 0.5
|
||||
end
|
358
application.c
358
application.c
|
@ -12,8 +12,8 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** STM32F1 application example
|
||||
* @file main.c
|
||||
/** STM32F1 Maxim DS2432 (1k-Bit Protected 1-Wire EEPROM with SHA-1 Engine) implementation
|
||||
* @file application.c
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @date 2016-2017
|
||||
*/
|
||||
|
@ -41,6 +41,7 @@
|
|||
#include "print.h" // printing utilities
|
||||
#include "usart.h" // USART utilities
|
||||
#include "usb_cdcacm.h" // USB CDC ACM utilities
|
||||
#include "onewire_slave.h" // 1-Wire utilities
|
||||
|
||||
#define WATCHDOG_PERIOD 10000 /**< watchdog period in ms */
|
||||
|
||||
|
@ -143,14 +144,14 @@ static void process_command(char* str)
|
|||
if (!word) {
|
||||
time_rtc = rtc_get_counter_val(); // get time from internal RTC
|
||||
time_tm = localtime(&time_rtc); // convert time
|
||||
printf("date: %d-%02d-%02d\n", 1900+time_tm->tm_year, time_tm->tm_mon, time_tm->tm_mday);
|
||||
printf("date: %d-%02d-%02d\n", 1900+time_tm->tm_year, time_tm->tm_mon+1, time_tm->tm_mday);
|
||||
} else if (strlen(word)!=10 || word[0]!='2' || word[1]!='0' || word[2]<'0' || word[2]>'9' || word[3]<'0' || word[3]>'9' || word[5]<'0' || word[5]>'1' || word[6]<'0' || word[6]>'9' || word[8]<'0' || word[8]>'3' || word[9]<'0' || word[9]>'9') {
|
||||
goto error;
|
||||
} else {
|
||||
time_rtc = rtc_get_counter_val(); // get time from internal RTC
|
||||
time_tm = localtime(&time_rtc); // convert time
|
||||
time_tm->tm_year = ((word[0]-'0')*1000+(word[1]-'0')*100+(word[2]-'0')*10+(word[3]-'0')*1)-1900; // set year
|
||||
time_tm->tm_mon = (word[5]-'0')*10+(word[6]-'0')*1; // set month
|
||||
time_tm->tm_mon = (word[5]-'0')*10+(word[6]-'0')*1-1; // set month
|
||||
time_tm->tm_mday = (word[8]-'0')*10+(word[9]-'0')*1; // set day
|
||||
time_rtc = mktime(time_tm); // get back seconds
|
||||
rtc_set_counter_val(time_rtc); // save time to internal RTC
|
||||
|
@ -166,6 +167,138 @@ error:
|
|||
return;
|
||||
}
|
||||
|
||||
/** static table used for the table_driven implementation
|
||||
* Generated by pycrc v0.9, https://pycrc.org
|
||||
* using the configuration:
|
||||
* Width = 16
|
||||
* Poly = 0x8005
|
||||
* Xor_In = 0x0000
|
||||
* ReflectIn = True
|
||||
* Xor_Out = 0xffff
|
||||
* ReflectOut = True
|
||||
* Algorithm = table-driven
|
||||
*/
|
||||
static const uint16_t crc_table[256] = {
|
||||
0x0000, 0xc0c1, 0xc181, 0x0140, 0xc301, 0x03c0, 0x0280, 0xc241,
|
||||
0xc601, 0x06c0, 0x0780, 0xc741, 0x0500, 0xc5c1, 0xc481, 0x0440,
|
||||
0xcc01, 0x0cc0, 0x0d80, 0xcd41, 0x0f00, 0xcfc1, 0xce81, 0x0e40,
|
||||
0x0a00, 0xcac1, 0xcb81, 0x0b40, 0xc901, 0x09c0, 0x0880, 0xc841,
|
||||
0xd801, 0x18c0, 0x1980, 0xd941, 0x1b00, 0xdbc1, 0xda81, 0x1a40,
|
||||
0x1e00, 0xdec1, 0xdf81, 0x1f40, 0xdd01, 0x1dc0, 0x1c80, 0xdc41,
|
||||
0x1400, 0xd4c1, 0xd581, 0x1540, 0xd701, 0x17c0, 0x1680, 0xd641,
|
||||
0xd201, 0x12c0, 0x1380, 0xd341, 0x1100, 0xd1c1, 0xd081, 0x1040,
|
||||
0xf001, 0x30c0, 0x3180, 0xf141, 0x3300, 0xf3c1, 0xf281, 0x3240,
|
||||
0x3600, 0xf6c1, 0xf781, 0x3740, 0xf501, 0x35c0, 0x3480, 0xf441,
|
||||
0x3c00, 0xfcc1, 0xfd81, 0x3d40, 0xff01, 0x3fc0, 0x3e80, 0xfe41,
|
||||
0xfa01, 0x3ac0, 0x3b80, 0xfb41, 0x3900, 0xf9c1, 0xf881, 0x3840,
|
||||
0x2800, 0xe8c1, 0xe981, 0x2940, 0xeb01, 0x2bc0, 0x2a80, 0xea41,
|
||||
0xee01, 0x2ec0, 0x2f80, 0xef41, 0x2d00, 0xedc1, 0xec81, 0x2c40,
|
||||
0xe401, 0x24c0, 0x2580, 0xe541, 0x2700, 0xe7c1, 0xe681, 0x2640,
|
||||
0x2200, 0xe2c1, 0xe381, 0x2340, 0xe101, 0x21c0, 0x2080, 0xe041,
|
||||
0xa001, 0x60c0, 0x6180, 0xa141, 0x6300, 0xa3c1, 0xa281, 0x6240,
|
||||
0x6600, 0xa6c1, 0xa781, 0x6740, 0xa501, 0x65c0, 0x6480, 0xa441,
|
||||
0x6c00, 0xacc1, 0xad81, 0x6d40, 0xaf01, 0x6fc0, 0x6e80, 0xae41,
|
||||
0xaa01, 0x6ac0, 0x6b80, 0xab41, 0x6900, 0xa9c1, 0xa881, 0x6840,
|
||||
0x7800, 0xb8c1, 0xb981, 0x7940, 0xbb01, 0x7bc0, 0x7a80, 0xba41,
|
||||
0xbe01, 0x7ec0, 0x7f80, 0xbf41, 0x7d00, 0xbdc1, 0xbc81, 0x7c40,
|
||||
0xb401, 0x74c0, 0x7580, 0xb541, 0x7700, 0xb7c1, 0xb681, 0x7640,
|
||||
0x7200, 0xb2c1, 0xb381, 0x7340, 0xb101, 0x71c0, 0x7080, 0xb041,
|
||||
0x5000, 0x90c1, 0x9181, 0x5140, 0x9301, 0x53c0, 0x5280, 0x9241,
|
||||
0x9601, 0x56c0, 0x5780, 0x9741, 0x5500, 0x95c1, 0x9481, 0x5440,
|
||||
0x9c01, 0x5cc0, 0x5d80, 0x9d41, 0x5f00, 0x9fc1, 0x9e81, 0x5e40,
|
||||
0x5a00, 0x9ac1, 0x9b81, 0x5b40, 0x9901, 0x59c0, 0x5880, 0x9841,
|
||||
0x8801, 0x48c0, 0x4980, 0x8941, 0x4b00, 0x8bc1, 0x8a81, 0x4a40,
|
||||
0x4e00, 0x8ec1, 0x8f81, 0x4f40, 0x8d01, 0x4dc0, 0x4c80, 0x8c41,
|
||||
0x4400, 0x84c1, 0x8581, 0x4540, 0x8701, 0x47c0, 0x4680, 0x8641,
|
||||
0x8201, 0x42c0, 0x4380, 0x8341, 0x4100, 0x81c1, 0x8081, 0x4040
|
||||
};
|
||||
|
||||
/** update the crc value with new data.
|
||||
* @param crc The current crc value.
|
||||
* @param data Pointer to a buffer of @a data_len bytes.
|
||||
* @param data_len Number of bytes in the @a data buffer.
|
||||
* @return The updated crc value.
|
||||
*/
|
||||
static uint16_t crc16_update(uint16_t crc, const uint8_t *data, size_t data_len)
|
||||
{
|
||||
const unsigned char *d = (const unsigned char *)data;
|
||||
unsigned int tbl_idx;
|
||||
|
||||
while (data_len--) {
|
||||
tbl_idx = (crc ^ *d) & 0xff;
|
||||
crc = (crc_table[tbl_idx] ^ (crc >> 8)) & 0xffff;
|
||||
|
||||
d++;
|
||||
}
|
||||
return crc & 0xffff;
|
||||
}
|
||||
|
||||
/** SHA-1 input data to calculate MAC for 'Read Authenticated Page' function command */
|
||||
static uint32_t m[16] = {0};
|
||||
/** intermediate calculation */
|
||||
uint32_t wt[80] = {0};
|
||||
|
||||
/** DS2432 SHA-1 S function, rotating to the left
|
||||
* @param[in] x value to rotate by @a n to the left
|
||||
* @param[in] n rotate @a x by n to the left
|
||||
* @return @a x rotated ny @a n to the left
|
||||
*/
|
||||
static uint32_t s_left(uint32_t x, uint8_t n) {
|
||||
return (((x)<<(n)) | ((x)>>(32-(n))));
|
||||
}
|
||||
|
||||
/** DS2432 SHA-1 F function
|
||||
* @param[in] t time/round
|
||||
* @param[in] b B value
|
||||
* @param[in] c C value
|
||||
* @param[in] d D value
|
||||
* @return result of the F function
|
||||
*/
|
||||
static uint32_t f(uint8_t t, uint32_t b, uint32_t c, uint32_t d) {
|
||||
if (t<20) {
|
||||
return (b&c)|((~b)&d);
|
||||
} else if (t<40) {
|
||||
return b^c^d;
|
||||
} else if (t<60) {
|
||||
return (b&c)|(b&d)|(c&d);
|
||||
} else if (t<80) {
|
||||
return b^c^d;
|
||||
} else { // this should not happen
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** DS2432 SHA-1 K function
|
||||
* @param[in] t time/round
|
||||
* @return result of the K function
|
||||
*/
|
||||
static uint32_t k(uint8_t t) {
|
||||
if (t<20) {
|
||||
return 0x5A827999;
|
||||
} else if (t<40) {
|
||||
return 0x6ED9EBA1;
|
||||
} else if (t<60) {
|
||||
return 0x8F1BBCDC;
|
||||
} else if (t<80) {
|
||||
return 0xCA62C1D6;
|
||||
} else { // this should not happen
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** DS2432 SHA-1 W function
|
||||
* @param[in] t time/round
|
||||
* @note uses @a m or previously calculated @a wt values
|
||||
* @return result of the W function
|
||||
*/
|
||||
static uint32_t w(uint8_t t) {
|
||||
if (t<16) {
|
||||
return m[t];
|
||||
} else {
|
||||
return s_left(wt[t-3]^wt[t-8]^wt[t-14]^wt[t-16], 1);
|
||||
}
|
||||
}
|
||||
|
||||
/** program entry point
|
||||
* this is the firmware function started by the micro-controller
|
||||
*/
|
||||
|
@ -174,7 +307,6 @@ void main(void)
|
|||
{
|
||||
rcc_clock_setup_in_hse_8mhz_out_72mhz(); // use 8 MHz high speed external clock to generate 72 MHz internal clock
|
||||
|
||||
|
||||
#if DEBUG
|
||||
// enable functionalities for easier debug
|
||||
DBGMCU_CR |= DBGMCU_CR_IWDG_STOP; // stop independent watchdog counter when code is halted
|
||||
|
@ -191,7 +323,7 @@ void main(void)
|
|||
board_setup(); // setup board
|
||||
usart_setup(); // setup USART (for printing)
|
||||
usb_cdcacm_setup(); // setup USB CDC ACM (for printing)
|
||||
printf("welcome to the CuVoodoo STM32F1 example application\n"); // print welcome message
|
||||
printf("welcome to the CuVoodoo STM32F1 DS2432 implementation (1k-Bit Protected 1-Wire EEPROM with SHA-1 Engine)\n"); // print welcome message
|
||||
|
||||
#if !(DEBUG)
|
||||
// show watchdog information
|
||||
|
@ -214,40 +346,68 @@ void main(void)
|
|||
|
||||
time_rtc= rtc_get_counter_val(); // get time from internal RTC
|
||||
time_tm = localtime(&time_rtc); // convert time
|
||||
printf("date: %d-%02d-%02d %02d:%02d:%02d\n", 1900+time_tm->tm_year, time_tm->tm_mon, time_tm->tm_mday, time_tm->tm_hour, time_tm->tm_min, time_tm->tm_sec);
|
||||
printf("date: %d-%02d-%02d %02d:%02d:%02d\n", 1900+time_tm->tm_year, time_tm->tm_mon+1, time_tm->tm_mday, time_tm->tm_hour, time_tm->tm_min, time_tm->tm_sec);
|
||||
|
||||
uint8_t ds2432_eeprom[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97}; /**< EEPROM content (including secret) */
|
||||
uint8_t ds2432_scratchpad[8]; /**< scratchpad data */
|
||||
uint8_t ds2432_address[2+1] = {0, 0, 0x5f}; /**< address registers: target address and E/S */
|
||||
uint8_t ds2432_buffer[2+1+8+2]; /**< buffer to save command code target address, data status, scratchpad, and CRC */
|
||||
const uint8_t ds2432_padding = 0xff; /**< padding byte used in Read Authenticated Page command */
|
||||
uint16_t ds2432_crc = 0; // CRC-16 used in 1-Wire DS2432 communication
|
||||
uint8_t ds2432_mac[20] = {0}; // buffer for the MAC
|
||||
enum {
|
||||
DS2432_STATE_IDLE,
|
||||
DS2432_STATE_WRITE_SCRATCHPAD_DATA,
|
||||
DS2432_STATE_WRITE_SCRATCHPAD_CRC,
|
||||
DS2432_STATE_READ_SCRATCHPAD_ADDRESS,
|
||||
DS2432_STATE_READ_SCRATCHPAD_DATA,
|
||||
DS2432_STATE_READ_SCRATCHPAD_CRC,
|
||||
DS2432_STATE_READ_AUTHENTICATED_PAGE_ADDRESS,
|
||||
DS2432_STATE_READ_AUTHENTICATED_PAGE_DATA,
|
||||
DS2432_STATE_READ_AUTHENTICATED_PAGE_PADDING,
|
||||
DS2432_STATE_READ_AUTHENTICATED_PAGE_CRC,
|
||||
DS2432_STATE_READ_AUTHENTICATED_PAGE_MAC,
|
||||
DS2432_STATE_READ_AUTHENTICATED_PAGE_MACCRC,
|
||||
DS2432_STATE_READ_MEMORY_ADDRESS,
|
||||
DS2432_STATE_READ_MEMORY_DATA,
|
||||
} ds2432_state = DS2432_STATE_IDLE; /**< current state */
|
||||
|
||||
printf("setup 1-Wire bus: ");
|
||||
onewire_slave_setup(ds2432_eeprom[0x90], ((uint64_t)ds2432_eeprom[0x91]<<0)+((uint64_t)ds2432_eeprom[0x91]<<0)+((uint64_t)ds2432_eeprom[0x92]<<8)+((uint64_t)ds2432_eeprom[0x93]<<16)+((uint64_t)ds2432_eeprom[0x94]<<24)+((uint64_t)ds2432_eeprom[0x95]<<32)+((uint64_t)ds2432_eeprom[0x96]<<40)); // setup 1-Wire peripheral to act as slave
|
||||
printf("OK\n");
|
||||
|
||||
// main loop
|
||||
printf("command input: ready\n");
|
||||
bool action = false; // if an action has been performed don't go to sleep
|
||||
button_flag = false; // reset button flag
|
||||
char c = '\0'; // to store received character
|
||||
char ch = '\0'; // to store received character
|
||||
bool char_flag = false; // a new character has been received
|
||||
while (true) { // infinite loop
|
||||
iwdg_reset(); // kick the dog
|
||||
while (usart_received) { // data received over UART
|
||||
action = true; // action has been performed
|
||||
led_toggle(); // toggle LED
|
||||
c = usart_getchar(); // store receive character
|
||||
ch = usart_getchar(); // store receive character
|
||||
char_flag = true; // notify character has been received
|
||||
}
|
||||
while (usb_cdcacm_received) { // data received over USB
|
||||
action = true; // action has been performed
|
||||
led_toggle(); // toggle LED
|
||||
c = usb_cdcacm_getchar(); // store receive character
|
||||
ch = usb_cdcacm_getchar(); // store receive character
|
||||
char_flag = true; // notify character has been received
|
||||
}
|
||||
while (char_flag) { // user data received
|
||||
char_flag = false; // reset flag
|
||||
action = true; // action has been performed
|
||||
printf("%c",c); // echo receive character
|
||||
if (c=='\r' || c=='\n') { // end of command received
|
||||
printf("%c",ch); // echo receive character
|
||||
if (ch=='\r' || ch=='\n') { // end of command received
|
||||
if (command_i>0) { // there is a command to process
|
||||
command[command_i] = 0; // end string
|
||||
command_i = 0; // prepare for next command
|
||||
process_command(command); // process user command
|
||||
}
|
||||
} else { // user command input
|
||||
command[command_i] = c; // save command input
|
||||
command[command_i] = ch; // save command input
|
||||
if (command_i<LENGTH(command)-2) { // verify if there is place to save next character
|
||||
command_i++; // save next character
|
||||
}
|
||||
|
@ -255,18 +415,15 @@ void main(void)
|
|||
}
|
||||
while (button_flag) { // user pressed button
|
||||
action = true; // action has been performed
|
||||
printf("button pressed\n");
|
||||
led_toggle(); // toggle LED
|
||||
for (uint32_t i=0; i<1000000; i++) { // wait a bit to remove noise and double trigger
|
||||
__asm__("nop");
|
||||
}
|
||||
printf("button pressed\n");
|
||||
button_flag = false; // reset flag
|
||||
}
|
||||
while (rtc_internal_tick_flag) { // the internal RTC ticked
|
||||
rtc_internal_tick_flag = false; // reset flag
|
||||
action = true; // action has been performed
|
||||
#if !defined(BLUE_PILL) // on the blue pill the LED is close to the 32.768 kHz oscillator and heavily influences it
|
||||
led_toggle(); // toggle LED (good to indicate if main function is stuck)
|
||||
//led_toggle(); // toggle LED (good to indicate if main function is stuck)
|
||||
#endif
|
||||
time_rtc = rtc_get_counter_val(); // get time from internal RTC (seconds since Unix Epoch)
|
||||
time_tm = localtime(&time_rtc); // get time in tm format from Epoch (time zones are not handled for non-POSIX environments)
|
||||
|
@ -274,6 +431,171 @@ void main(void)
|
|||
printf("time: %02d:%02d:%02d\n", time_tm->tm_hour, time_tm->tm_min, time_tm->tm_sec);
|
||||
}
|
||||
}
|
||||
while (onewire_slave_function_code_received) { // we received a function command code over the 1-Wire bus
|
||||
onewire_slave_function_code_received = false; // reset flag
|
||||
action = true; // action has been performed
|
||||
switch (onewire_slave_function_code) {
|
||||
case 0x0f: // Write Scratchpad
|
||||
onewire_slave_function_read(ds2432_buffer, 2+8); // read target address and scratchpad
|
||||
ds2432_state = DS2432_STATE_WRITE_SCRATCHPAD_DATA; // update state
|
||||
break;
|
||||
case 0xaa: // Read Scratchpad
|
||||
onewire_slave_function_write(ds2432_address, LENGTH(ds2432_address)); // send address registers
|
||||
ds2432_state = DS2432_STATE_READ_SCRATCHPAD_ADDRESS; // update state
|
||||
break;
|
||||
case 0xa5: // Read Authenticated Page
|
||||
onewire_slave_function_read(ds2432_buffer, 2); // read target address
|
||||
ds2432_state = DS2432_STATE_READ_AUTHENTICATED_PAGE_ADDRESS; // update state
|
||||
break;
|
||||
case 0xf0: // Read Memory
|
||||
onewire_slave_function_read(ds2432_buffer, 2); // read target address
|
||||
ds2432_state = DS2432_STATE_READ_MEMORY_ADDRESS; // update state
|
||||
break;
|
||||
default: // unknown function command code
|
||||
ds2432_state = DS2432_STATE_IDLE; // return to idle state
|
||||
break;
|
||||
}
|
||||
printf("1-Wire function command received: 0x%02x\n", onewire_slave_function_code);
|
||||
}
|
||||
while (onewire_slave_transfer_complete) { // the current data transfer completed
|
||||
onewire_slave_transfer_complete = false; // reset flag
|
||||
action = true; // action has been performed
|
||||
switch (ds2432_state) {
|
||||
case DS2432_STATE_WRITE_SCRATCHPAD_DATA:
|
||||
ds2432_crc = crc16_update(0, (const uint8_t *)&onewire_slave_function_code, 1); // initialize CRC with function code
|
||||
ds2432_crc = crc16_update(ds2432_crc, ds2432_buffer, (2+8)); // calculate CRC
|
||||
ds2432_crc ^= 0xffff; // invert CRC
|
||||
ds2432_buffer[2+1+8+0] = ds2432_crc>>0; // save CRC (LSB first)
|
||||
ds2432_buffer[2+1+8+1] = ds2432_crc>>8; // save CRC (MSB last)
|
||||
onewire_slave_function_write(&ds2432_buffer[2+1+8+0], 2); // write CRC
|
||||
ds2432_state = DS2432_STATE_WRITE_SCRATCHPAD_CRC; // update state
|
||||
ds2432_address[0] = ds2432_buffer[0]&0xf8; // save target address
|
||||
ds2432_address[1] = ds2432_buffer[1]; // save target address
|
||||
ds2432_address[2] = 0x5f; // reset state
|
||||
for (uint8_t i=0; i<LENGTH(ds2432_scratchpad) && i<LENGTH(ds2432_buffer)-2; i++) {
|
||||
ds2432_scratchpad[i] = ds2432_buffer[i+2]; // save scratchpad
|
||||
}
|
||||
break;
|
||||
case DS2432_STATE_READ_SCRATCHPAD_ADDRESS:
|
||||
onewire_slave_function_write(ds2432_scratchpad, LENGTH(ds2432_scratchpad)); // send scratchpad
|
||||
ds2432_state = DS2432_STATE_READ_SCRATCHPAD_DATA; // update state
|
||||
ds2432_crc = crc16_update(0, (const uint8_t *)&onewire_slave_function_code, 1); // initialize CRC with function code
|
||||
ds2432_crc = crc16_update(ds2432_crc, ds2432_address, LENGTH(ds2432_address)); // calculate CRC
|
||||
ds2432_crc = crc16_update(ds2432_crc, ds2432_scratchpad, LENGTH(ds2432_scratchpad)); // calculate CRC
|
||||
ds2432_crc ^= 0xffff; // invert CRC
|
||||
ds2432_buffer[2+1+8+0] = ds2432_crc>>0; // save CRC (LSB first)
|
||||
ds2432_buffer[2+1+8+1] = ds2432_crc>>8; // save CRC (MSB last)
|
||||
break;
|
||||
case DS2432_STATE_READ_SCRATCHPAD_DATA: // scratchpad data transfer complete, send CRC
|
||||
onewire_slave_function_write(&ds2432_buffer[2+1+8+0], 2); // send CRC
|
||||
ds2432_state = DS2432_STATE_WRITE_SCRATCHPAD_CRC; // update state
|
||||
break;
|
||||
case DS2432_STATE_READ_AUTHENTICATED_PAGE_ADDRESS: // target address transfer completed, send data
|
||||
if (0==ds2432_buffer[1] && ds2432_buffer[0]<0x80) { // target address matches to memory page
|
||||
onewire_slave_function_write(&ds2432_eeprom[ds2432_buffer[0]], 0x20-(ds2432_buffer[0]&0x1f)); // send memory data until end of page
|
||||
ds2432_state = DS2432_STATE_READ_AUTHENTICATED_PAGE_DATA; // update state
|
||||
} else { // target address is out of range
|
||||
ds2432_state = DS2432_STATE_IDLE; // return to idle state
|
||||
}
|
||||
break;
|
||||
case DS2432_STATE_READ_AUTHENTICATED_PAGE_DATA: // memory page data transfer completed, send padding byte
|
||||
onewire_slave_function_write((uint8_t *)&ds2432_padding, 1); // send padding byte
|
||||
ds2432_state = DS2432_STATE_READ_AUTHENTICATED_PAGE_PADDING; // update state
|
||||
ds2432_crc = crc16_update(0, (const uint8_t *)&onewire_slave_function_code, 1); // initialize CRC with function code
|
||||
ds2432_crc = crc16_update(ds2432_crc, ds2432_buffer, 2); // update CRC with target address
|
||||
ds2432_crc = crc16_update(ds2432_crc, &ds2432_eeprom[ds2432_buffer[0]], 0x20-(ds2432_buffer[0]&0x1f)); // update CRC with data
|
||||
ds2432_crc = crc16_update(ds2432_crc, &ds2432_padding, 1); // update CRC with padding
|
||||
ds2432_crc ^= 0xffff; // invert CRC
|
||||
ds2432_buffer[2+1+8+0] = ds2432_crc>>0; // save CRC (LSB first)
|
||||
ds2432_buffer[2+1+8+1] = ds2432_crc>>8; // save CRC (MSB last)
|
||||
break;
|
||||
case DS2432_STATE_READ_AUTHENTICATED_PAGE_PADDING: // padding byte transfer complete, send CRC
|
||||
onewire_slave_function_write(&ds2432_buffer[2+1+8+0], 2); // send CRC
|
||||
ds2432_state = DS2432_STATE_READ_AUTHENTICATED_PAGE_CRC; // update state
|
||||
// calculate MAC
|
||||
{
|
||||
ds2432_buffer[0] &= 0xe0; // set target address to start of page
|
||||
// initialize SHA-1 input data (see table 4)
|
||||
m[0] = (ds2432_eeprom[0x80+0]<<24)+(ds2432_eeprom[0x80+1]<<16)+(ds2432_eeprom[0x80+2]<<8)+(ds2432_eeprom[0x80+3]<<0); // copy secret
|
||||
for (uint8_t i=0; i<8; i++) { // copy page
|
||||
m[1+i] = (ds2432_eeprom[ds2432_buffer[0]+i*4+0]<<24)+(ds2432_eeprom[ds2432_buffer[0]+i*4+1]<<16)+(ds2432_eeprom[ds2432_buffer[0]+i*4+2]<<8)+(ds2432_eeprom[ds2432_buffer[0]+i*4+3]<<0);
|
||||
}
|
||||
m[9] = (0xff<<24)+(0xff<<16)+(0xff<<8)+(0xff<<0); // filling bytes
|
||||
m[10] = ((0x40+(ds2432_buffer[0]>>5))<<24)+(ds2432_eeprom[0x90+0]<<16)+(ds2432_eeprom[0x90+1]<<8)+(ds2432_eeprom[0x90+2]<<0); // copy target and ROM code
|
||||
m[11] = (ds2432_eeprom[0x90+3]<<24)+(ds2432_eeprom[0x90+4]<<16)+(ds2432_eeprom[0x90+5]<<8)+(ds2432_eeprom[0x90+6]<<0); // copy ROM code
|
||||
m[12] = (ds2432_eeprom[0x80+4]<<24)+(ds2432_eeprom[0x80+5]<<16)+(ds2432_eeprom[0x80+6]<<8)+(ds2432_eeprom[0x80+7]<<0); // copy rest of secret
|
||||
m[13] = (ds2432_scratchpad[4]<<24)+(ds2432_scratchpad[5]<<16)+(ds2432_scratchpad[6]<<8)+(0x80<<0); // copy challenge
|
||||
m[14] = (0x00<<24)+(0x00<<16)+(0x00<<8)+(0x00<<0);
|
||||
m[15] = (0x00<<24)+(0x00<<16)+(0x01<<8)+(0xb8<<0);
|
||||
|
||||
// initialize variables
|
||||
uint32_t a = 0x67452301;
|
||||
uint32_t b = 0xEFCDAB89;
|
||||
uint32_t c = 0x98BADCFE;
|
||||
uint32_t d = 0x10325476;
|
||||
uint32_t e = 0xC3D2E1F0;
|
||||
|
||||
// loop through computation
|
||||
for (uint8_t t=0; t<80; t++) {
|
||||
wt[t] = w(t);
|
||||
uint32_t tmp = s_left(a, 5)+f(t,b,c,d)+wt[t]+k(t)+e;
|
||||
e = d;
|
||||
d = c;
|
||||
c = s_left(b, 30);
|
||||
b = a;
|
||||
a = tmp;
|
||||
}
|
||||
|
||||
// copy result
|
||||
ds2432_mac[0] = e>>0;
|
||||
ds2432_mac[1] = e>>8;
|
||||
ds2432_mac[2] = e>>16;
|
||||
ds2432_mac[3] = e>>24;
|
||||
ds2432_mac[4] = d>>0;
|
||||
ds2432_mac[5] = d>>8;
|
||||
ds2432_mac[6] = d>>16;
|
||||
ds2432_mac[7] = d>>24;
|
||||
ds2432_mac[8] = c>>0;
|
||||
ds2432_mac[9] = c>>8;
|
||||
ds2432_mac[10] = c>>16;
|
||||
ds2432_mac[11] = c>>24;
|
||||
ds2432_mac[12] = b>>0;
|
||||
ds2432_mac[13] = b>>8;
|
||||
ds2432_mac[14] = b>>16;
|
||||
ds2432_mac[15] = b>>24;
|
||||
ds2432_mac[16] = a>>0;
|
||||
ds2432_mac[17] = a>>8;
|
||||
ds2432_mac[18] = a>>16;
|
||||
ds2432_mac[19] = a>>24;
|
||||
}
|
||||
|
||||
break;
|
||||
case DS2432_STATE_READ_AUTHENTICATED_PAGE_CRC: // CRC transfer completed, send MAC
|
||||
onewire_slave_function_write(ds2432_mac, LENGTH(ds2432_mac)); // send CRC
|
||||
ds2432_state = DS2432_STATE_READ_AUTHENTICATED_PAGE_MAC; // update state
|
||||
ds2432_crc = crc16_update(0, ds2432_mac, LENGTH(ds2432_mac)); // calculate CRC for MAC
|
||||
ds2432_crc ^= 0xffff; // invert CRC
|
||||
ds2432_buffer[2+1+8+0] = ds2432_crc>>0; // save CRC (LSB first)
|
||||
ds2432_buffer[2+1+8+1] = ds2432_crc>>8; // save CRC (MSB last)
|
||||
break;
|
||||
case DS2432_STATE_READ_AUTHENTICATED_PAGE_MAC: // padding byte transfer complete, send CRC
|
||||
onewire_slave_function_write(&ds2432_buffer[2+1+8+0], 2); // send CRC
|
||||
ds2432_state = DS2432_STATE_READ_AUTHENTICATED_PAGE_MACCRC; // update state
|
||||
break;
|
||||
case DS2432_STATE_READ_MEMORY_ADDRESS:
|
||||
if (0==ds2432_buffer[1] && ds2432_buffer[0]<0x97) { // target address is in EEPROM range
|
||||
onewire_slave_function_write(&ds2432_eeprom[ds2432_buffer[0]], LENGTH(ds2432_eeprom)-ds2432_buffer[0]); // send memory data until end of page
|
||||
ds2432_state = DS2432_STATE_READ_MEMORY_DATA; // update state
|
||||
} else { // target address is out of range
|
||||
ds2432_state = DS2432_STATE_IDLE; // return to idle state
|
||||
}
|
||||
break;
|
||||
default: // unknown or end state
|
||||
ds2432_state = DS2432_STATE_IDLE; // return to idle state
|
||||
break;
|
||||
}
|
||||
printf("1-Wire transfer complete\n");
|
||||
}
|
||||
if (action) { // go to sleep if nothing had to be done, else recheck for activity
|
||||
action = false;
|
||||
} else {
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
/* linker script for application running on STM32F103x8 micro-controller
|
||||
* the STM32F103x8 has 64kB of flash starting at 0x0800 0000, and 20kB of RAM starting at 0x2000 0000
|
||||
* the bootloader will take the first 9 kB of flash, followed by the application
|
||||
* the USB DFU bootloader will take the first 8 kB of flash, followed by the application
|
||||
*/
|
||||
|
||||
/* Define memory regions. */
|
||||
MEMORY
|
||||
{
|
||||
rom (rx) : ORIGIN = 0x08000000 + 9K, LENGTH = 55K
|
||||
rom (rx) : ORIGIN = 0x08000000 + 8K, LENGTH = 56K
|
||||
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
|
||||
}
|
||||
PROVIDE(__application_beginning = ORIGIN(rom));
|
||||
|
|
|
@ -74,7 +74,7 @@ void main(void)
|
|||
if (!dfu_force && (((*application)&0xFFFE0000)==0x20000000)) { // application at address seems valid
|
||||
SCB_VTOR = (volatile uint32_t)(application); // set vector table to application vector table (store at the beginning of the application)
|
||||
__asm__ volatile ("MSR msp,%0" : :"r"(*application)); // set stack pointer to address provided in the beginning of the application (loaded into a register first)
|
||||
(*(void(**)())(application + 1))(); // start application (by jumping to the reset function which address is stored as second entry of the vector table)
|
||||
(*(void(**)(void))(application + 1))(); // start application (by jumping to the reset function which address is stored as second entry of the vector table)
|
||||
}
|
||||
|
||||
rcc_clock_setup_in_hse_8mhz_out_72mhz(); // start main clock
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
/* linker script for application running on STM32F103x8 micro-controller
|
||||
* the STM32F103x8 has 64kB of flash starting at 0x0800 0000, and 20kB of RAM starting at 0x2000 0000
|
||||
* the bootloader will take the first 9 kB of flash, followed by the application
|
||||
* the USB DFU bootloader will take the first 8 kB of flash, followed by the application
|
||||
*/
|
||||
|
||||
/* Define memory regions. */
|
||||
MEMORY
|
||||
{
|
||||
rom (rx) : ORIGIN = 0x08000000, LENGTH = 9K
|
||||
rom (rx) : ORIGIN = 0x08000000, LENGTH = 8K
|
||||
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
|
||||
}
|
||||
PROVIDE(__application_beginning = ORIGIN(rom) + LENGTH(rom));
|
||||
PROVIDE(__application_end = __application_beginning + 55K);
|
||||
PROVIDE(__application_end = __application_beginning + 56K);
|
||||
|
||||
/* include rest of the definitions for the STM32F1 family */
|
||||
INCLUDE libopencm3_stm32f1.ld
|
||||
|
|
45
global.c
45
global.c
|
@ -15,23 +15,26 @@
|
|||
/** global definitions and methods (code)
|
||||
* @file global.c
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @date 2016
|
||||
* @date 2016-2017
|
||||
*/
|
||||
/* standard libraries */
|
||||
#include <stdint.h> // standard integer types
|
||||
#include <stdlib.h> // general utilities
|
||||
|
||||
/* STM32 (including CM3) libraries */
|
||||
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
|
||||
#include <libopencm3/cm3/nvic.h> // interrupt handler
|
||||
#include <libopencm3/cm3/systick.h> // SysTick library
|
||||
#include <libopencm3/stm32/rcc.h> // real-time control clock library
|
||||
#include <libopencm3/stm32/gpio.h> // general purpose input output library
|
||||
#include <libopencm3/stm32/timer.h> // timer library
|
||||
#include <libopencm3/cm3/nvic.h> // interrupt handler
|
||||
#include <libopencm3/stm32/exti.h> // external interrupt defines
|
||||
|
||||
#include "global.h" // common methods
|
||||
#include "string.h" // memory utilities
|
||||
|
||||
volatile bool button_flag = false;
|
||||
volatile uint32_t sleep_duration = 0; /**< sleep duration count down (in SysTick interrupts) */
|
||||
|
||||
char* b2s(uint64_t binary, uint8_t rjust)
|
||||
{
|
||||
|
@ -79,6 +82,44 @@ void led_toggle(void)
|
|||
gpio_toggle(GPIO(LED_PORT), GPIO(LED_PIN));
|
||||
}
|
||||
|
||||
void sleep_us(uint32_t duration)
|
||||
{
|
||||
systick_counter_disable(); // disable SysTick to reconfigure it
|
||||
systick_clear(); // reset SysTick
|
||||
systick_set_frequency(1000000,rcc_ahb_frequency); // set SysTick frequency to microseconds
|
||||
systick_interrupt_enable(); // enable interrupt to count duration
|
||||
sleep_duration = duration; // save sleep duration for count down
|
||||
systick_counter_enable(); // start counting
|
||||
while (sleep_duration) { // wait for count down to complete
|
||||
__WFI(); // go to sleep
|
||||
}
|
||||
}
|
||||
|
||||
void sleep_ms(uint32_t duration)
|
||||
{
|
||||
systick_counter_disable(); // disable SysTick to reconfigure it
|
||||
systick_clear(); // reset SysTick
|
||||
systick_set_frequency(1000,rcc_ahb_frequency); // set SysTick frequency to milliseconds
|
||||
systick_interrupt_enable(); // enable interrupt to count duration
|
||||
sleep_duration = duration; // save sleep duration for count down
|
||||
systick_counter_enable(); // start counting
|
||||
while (sleep_duration) { // wait for count down to complete
|
||||
__WFI(); // go to sleep
|
||||
}
|
||||
}
|
||||
|
||||
/** SysTick interrupt handler */
|
||||
void sys_tick_handler(void)
|
||||
{
|
||||
if (sleep_duration) {
|
||||
sleep_duration--; // decrement duration
|
||||
}
|
||||
if (0==sleep_duration) { // sleep complete
|
||||
systick_counter_disable(); // stop systick
|
||||
systick_interrupt_disable(); // stop interrupting
|
||||
}
|
||||
}
|
||||
|
||||
void board_setup(void)
|
||||
{
|
||||
// setup LED
|
||||
|
|
10
global.h
10
global.h
|
@ -362,6 +362,16 @@ void led_off(void);
|
|||
/** toggle board LED */
|
||||
void led_toggle(void);
|
||||
|
||||
/** go to sleep for some microseconds
|
||||
* @param[in] duration sleep duration in us
|
||||
*/
|
||||
void sleep_us(uint32_t duration);
|
||||
|
||||
/** go to sleep for some milliseconds
|
||||
* @param[in] duration sleep duration in ms
|
||||
*/
|
||||
void sleep_ms(uint32_t duration);
|
||||
|
||||
/** setup board peripherals */
|
||||
void board_setup(void);
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
*
|
||||
*/
|
||||
/** library to communicate using I2C as master (code)
|
||||
* @file i2c_master.h
|
||||
* @file i2c_master.c
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @date 2017
|
||||
* @note peripherals used: I2C @ref i2c_master_i2c, timer @ref i2c_master_timer
|
||||
|
|
|
@ -17,13 +17,14 @@
|
|||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @date 2017
|
||||
* @note peripherals used: timer @ref onewire_master_timer, GPIO @ref onewire_master_gpio
|
||||
* @warning this library does not support parasite power mode
|
||||
* @note overdrive mode is not provided
|
||||
* @implements 1-Wire protocol description from Book of iButton Standards
|
||||
*/
|
||||
|
||||
/* standard libraries */
|
||||
#include <stdint.h> // standard integer types
|
||||
#include <stdbool.h> // boolean type
|
||||
#include <stdlib.h> // memory utilities
|
||||
#include <stddef.h> // NULL definition
|
||||
|
||||
/* STM32 (including CM3) libraries */
|
||||
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
|
||||
|
@ -43,7 +44,7 @@
|
|||
/** @} */
|
||||
|
||||
/** @defgroup onewire_master_gpio GPIO used for 1-wire signal
|
||||
* @note use external pull-up resistor on pin
|
||||
* @note external pull-up resistor on pin is required (< 5 kOhm)
|
||||
* @{
|
||||
*/
|
||||
#define ONEWIRE_MASTER_PORT A /**< GPIO port */
|
||||
|
@ -62,17 +63,25 @@ volatile enum {
|
|||
ONEWIRE_MAX /** to count the number of possible states */
|
||||
} onewire_master_state = ONEWIRE_STATE_IDLE;
|
||||
|
||||
volatile bool slave_presence = false; /**< if slaves have been detected */
|
||||
uint8_t* buffer = NULL; /**< input/output buffer for read/write commands/functions */
|
||||
size_t buffer_size = 0; /**< size of buffer in bits */
|
||||
volatile size_t buffer_bit = 0; /**< number of bits read/written */
|
||||
static volatile bool slave_presence = false; /**< if slaves have been detected */
|
||||
static uint8_t* buffer = NULL; /**< input/output buffer for read/write commands/functions */
|
||||
static uint32_t buffer_size = 0; /**< size of buffer in bits */
|
||||
static volatile uint32_t buffer_bit = 0; /**< number of bits read/written */
|
||||
static bool onewire_master_parasite = false; /**< if parasite power should be provided whenever the is no communication */
|
||||
static uint16_t onewire_master_recovery = 0; /**< the recovery time in us (1 < Trec) */
|
||||
|
||||
void onewire_master_setup(void)
|
||||
void onewire_master_setup(bool parasite, uint16_t recovery)
|
||||
{
|
||||
// setup GPIO with external interrupt
|
||||
rcc_periph_clock_enable(RCC_GPIO(ONEWIRE_MASTER_PORT)); // enable clock for GPIO peripheral
|
||||
gpio_set_mode(GPIO(ONEWIRE_MASTER_PORT), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO(ONEWIRE_MASTER_PIN)); // setup GPIO pin as output (master starts communication before slave replies)
|
||||
gpio_set(GPIO(ONEWIRE_MASTER_PORT),GPIO(ONEWIRE_MASTER_PIN)); // idle is high (using pull-up resistor)
|
||||
onewire_master_parasite = parasite; // save if we should provide parasite power
|
||||
// setup GPIO pin as output (master starts communication before slave replies)
|
||||
if (onewire_master_parasite) {
|
||||
gpio_set_mode(GPIO(ONEWIRE_MASTER_PORT), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(ONEWIRE_MASTER_PIN)); // provide parasite power (external pull-up resistor is still require for communication)
|
||||
} else {
|
||||
gpio_set_mode(GPIO(ONEWIRE_MASTER_PORT), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO(ONEWIRE_MASTER_PIN)); // normal 1-Wire communication (only using external pull-up resistor)
|
||||
}
|
||||
|
||||
// setup timer to generate/measure signal timing
|
||||
rcc_periph_clock_enable(RCC_TIM(ONEWIRE_MASTER_TIMER)); // enable clock for timer peripheral
|
||||
|
@ -81,10 +90,19 @@ void onewire_master_setup(void)
|
|||
timer_set_prescaler(TIM(ONEWIRE_MASTER_TIMER), 1-1); // don't use prescale since this 16 bits timer allows to wait > 480 us used for the reset pulse ( 1/(72E6/1/(2**16))=910us )
|
||||
|
||||
// use comparator to time signal (without using the output), starting at slot start
|
||||
timer_set_oc_value(TIM(ONEWIRE_MASTER_TIMER), TIM_OC1, 1*(rcc_ahb_frequency/1000000)); // use compare function to time master pulling low (1 < Tlowr < 15)
|
||||
timer_set_oc_value(TIM(ONEWIRE_MASTER_TIMER), TIM_OC2, 7*(rcc_ahb_frequency/1000000)); // use compare function to read of write (1 < Trw < 15)
|
||||
timer_set_oc_value(TIM(ONEWIRE_MASTER_TIMER), TIM_OC3, 62*(rcc_ahb_frequency/1000000)); // use compare function to end time slot (60 < Tslot < 120), this will be followed by a recovery time (end of timer)
|
||||
timer_set_oc_value(TIM(ONEWIRE_MASTER_TIMER), TIM_OC4, 120*(rcc_ahb_frequency/1000000)); // use compare function to detect slave presence (60 < Tpdl < 240)
|
||||
timer_set_oc_value(TIM(ONEWIRE_MASTER_TIMER), TIM_OC1, 1*(rcc_ahb_frequency/1000000)-1); // use compare function to time master pulling low when reading (1 < Tlowr < 15)
|
||||
timer_set_oc_value(TIM(ONEWIRE_MASTER_TIMER), TIM_OC2, 7*(rcc_ahb_frequency/1000000)-1); // use compare function to read or write 0 or 1 (1 < Trw < 15)
|
||||
timer_set_oc_value(TIM(ONEWIRE_MASTER_TIMER), TIM_OC3, 62*(rcc_ahb_frequency/1000000)-1); // use compare function to end time slot (60 < Tslot < 120), this will be followed by a recovery time (end of timer)
|
||||
timer_set_oc_value(TIM(ONEWIRE_MASTER_TIMER), TIM_OC4, (70-10)*(rcc_ahb_frequency/1000000)-1); // use compare function to detect slave presence (15 < Tpdh < 60 + 60 < Tpdl < 240), with hand tunig
|
||||
onewire_master_recovery = 5; // set minimum recovery time
|
||||
if (recovery>onewire_master_recovery) {
|
||||
onewire_master_recovery = recovery; // save desired recovery time
|
||||
}
|
||||
if (UINT16_MAX/onewire_master_recovery<(rcc_ahb_frequency/1000000)) { // catch integer overflow
|
||||
onewire_master_recovery = UINT16_MAX; // save maximum value
|
||||
} else {
|
||||
onewire_master_recovery *= (rcc_ahb_frequency/1000000); // save actual recovery time value
|
||||
}
|
||||
|
||||
timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_UIF); // clear update (overflow) flag
|
||||
timer_update_on_overflow(TIM(ONEWIRE_MASTER_TIMER)); // only use counter overflow as UEV source (use overflow as start time or timeout)
|
||||
|
@ -100,11 +118,12 @@ bool onewire_master_reset(void)
|
|||
// prepare timer
|
||||
timer_disable_counter(TIM(ONEWIRE_MASTER_TIMER)); // disable timer to reconfigure it
|
||||
timer_set_counter(TIM(ONEWIRE_MASTER_TIMER),0); // reset counter
|
||||
timer_set_period(TIM(ONEWIRE_MASTER_TIMER), 490*(rcc_ahb_frequency/1000000)); // set timeout to > 480 us (490)
|
||||
timer_set_period(TIM(ONEWIRE_MASTER_TIMER), 490*(rcc_ahb_frequency/1000000)-1); // set timeout to > 480 us (480 < Trst)
|
||||
|
||||
slave_presence = false; // reset state
|
||||
onewire_master_state = ONEWIRE_STATE_MASTER_RESET; // set new state
|
||||
|
||||
gpio_set_mode(GPIO(ONEWIRE_MASTER_PORT), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO(ONEWIRE_MASTER_PIN)); // normal 1-Wire communication (only using external pull-up resistor)
|
||||
gpio_clear(GPIO(ONEWIRE_MASTER_PORT),GPIO(ONEWIRE_MASTER_PIN)); // pull signal low to start reset (it's not important if it was low in the first place since the reset pulse has no maximum time)
|
||||
timer_enable_counter(TIM(ONEWIRE_MASTER_TIMER)); // start timer
|
||||
|
||||
|
@ -119,7 +138,7 @@ bool onewire_master_reset(void)
|
|||
}
|
||||
|
||||
/** write bits on 1-Wire bus
|
||||
* @warning buffer_size must be set to the number of bits to write and buffer must contain the data to write
|
||||
* @warning buffer_size must be set to the number of bits to writen and buffer must contain the data to write
|
||||
* @return if write succeeded
|
||||
*/
|
||||
static bool onewire_master_write(void)
|
||||
|
@ -130,16 +149,23 @@ static bool onewire_master_write(void)
|
|||
// prepare timer
|
||||
timer_disable_counter(TIM(ONEWIRE_MASTER_TIMER)); // disable timer to reconfigure it
|
||||
timer_set_counter(TIM(ONEWIRE_MASTER_TIMER),0); // reset counter
|
||||
timer_set_period(TIM(ONEWIRE_MASTER_TIMER), TIM_CCR3(TIM(ONEWIRE_MASTER_TIMER))+5*(rcc_ahb_frequency/1000000)); // set time for new time slot (Trec>1, after time slot end)
|
||||
uint16_t timeout = TIM_CCR3(TIM(ONEWIRE_MASTER_TIMER)); // time until new slot (= end of slot+recovery)
|
||||
if (UINT16_MAX-timeout<onewire_master_recovery) { // catch integer overflow
|
||||
timeout = UINT16_MAX; // set maximum value
|
||||
} else {
|
||||
timeout += onewire_master_recovery; // add recovery time to end of slot
|
||||
}
|
||||
timer_set_period(TIM(ONEWIRE_MASTER_TIMER), timeout-1); // set time for new time slot (Trec>1, after time slot end and recovery time)
|
||||
timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC2IF); // clear output compare flag
|
||||
timer_enable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC2IE); // enable compare interrupt for bit setting
|
||||
timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC3IF); // clear output compare flag
|
||||
timer_enable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC3IE); // enable compare interrupt for end of time slow
|
||||
|
||||
// start writing
|
||||
gpio_set_mode(GPIO(ONEWIRE_MASTER_PORT), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO(ONEWIRE_MASTER_PIN)); // normal 1-Wire communication (only using external pull-up resistor)
|
||||
gpio_clear(GPIO(ONEWIRE_MASTER_PORT),GPIO(ONEWIRE_MASTER_PIN)); // pull signal low to start slot
|
||||
timer_enable_counter(TIM(ONEWIRE_MASTER_TIMER)); // start timer
|
||||
while (onewire_master_state!=ONEWIRE_STATE_DONE && onewire_master_state!=ONEWIRE_STATE_ERROR) { // wait until reset procedure completed
|
||||
while (onewire_master_state!=ONEWIRE_STATE_DONE && onewire_master_state!=ONEWIRE_STATE_ERROR) { // wait until write procedure completed
|
||||
__WFI(); // go to sleep
|
||||
}
|
||||
if (ONEWIRE_STATE_ERROR==onewire_master_state) { // an error occurred
|
||||
|
@ -157,14 +183,19 @@ static bool onewire_master_read(void)
|
|||
if (0==buffer_size) { // check input
|
||||
return false;
|
||||
}
|
||||
if (!(buffer=realloc(buffer, (buffer_size-1)/8+1))) { // allocate memory
|
||||
return false; // error in memory allocation
|
||||
}
|
||||
buffer_bit = 0; // reset bit index
|
||||
onewire_master_state = ONEWIRE_STATE_MASTER_READ; // set new state
|
||||
|
||||
// prepare timer
|
||||
timer_disable_counter(TIM(ONEWIRE_MASTER_TIMER)); // disable timer to reconfigure it
|
||||
timer_set_counter(TIM(ONEWIRE_MASTER_TIMER),0); // reset counter
|
||||
uint16_t timeout = TIM_CCR3(TIM(ONEWIRE_MASTER_TIMER)); // time until new slot (= end of slot+recovery)
|
||||
if (UINT16_MAX-timeout<onewire_master_recovery) { // catch integer overflow
|
||||
timeout = UINT16_MAX; // set maximum value
|
||||
} else {
|
||||
timeout += onewire_master_recovery; // add recovery time to end of slot
|
||||
}
|
||||
timer_set_period(TIM(ONEWIRE_MASTER_TIMER), timeout-1); // set time for new time slot (Trec>1, after time slot end and recovery time)
|
||||
timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC1IF); // clear output compare flag
|
||||
timer_enable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC1IE); // enable compare interrupt for stop pulling low
|
||||
timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC2IF); // clear output compare flag
|
||||
|
@ -173,9 +204,10 @@ static bool onewire_master_read(void)
|
|||
timer_enable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC3IE); // enable compare interrupt for end of time slow
|
||||
|
||||
// start reading
|
||||
gpio_set_mode(GPIO(ONEWIRE_MASTER_PORT), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO(ONEWIRE_MASTER_PIN)); // normal 1-Wire communication (only using external pull-up resistor)
|
||||
gpio_clear(GPIO(ONEWIRE_MASTER_PORT),GPIO(ONEWIRE_MASTER_PIN)); // pull signal low to start slot
|
||||
timer_enable_counter(TIM(ONEWIRE_MASTER_TIMER)); // start timer
|
||||
while (onewire_master_state!=ONEWIRE_STATE_DONE && onewire_master_state!=ONEWIRE_STATE_ERROR) { // wait until reset procedure completed
|
||||
while (onewire_master_state!=ONEWIRE_STATE_DONE && onewire_master_state!=ONEWIRE_STATE_ERROR) { // wait until read procedure completed
|
||||
__WFI(); // go to sleep
|
||||
}
|
||||
if (ONEWIRE_STATE_ERROR==onewire_master_state) { // an error occurred
|
||||
|
@ -184,14 +216,14 @@ static bool onewire_master_read(void)
|
|||
return true;
|
||||
}
|
||||
|
||||
uint8_t onewire_master_crc(uint8_t* data, size_t size)
|
||||
uint8_t onewire_master_crc(uint8_t* data, uint32_t length)
|
||||
{
|
||||
if (NULL==data || 0==size) { // check input
|
||||
if (NULL==data || 0==length) { // check input
|
||||
return 0; // wrong input
|
||||
}
|
||||
|
||||
uint8_t crc = 0x00; // initial value
|
||||
for (uint8_t i=0; i<size; i++) { // go through every byte
|
||||
for (uint8_t i=0; i<length; i++) { // go through every byte
|
||||
crc ^= data[i]; // XOR byte
|
||||
for (uint8_t b=0; b<8; b++) { // go through every bit
|
||||
if (crc&0x01) { // least significant bit is set (we are using the reverse way)
|
||||
|
@ -204,14 +236,11 @@ uint8_t onewire_master_crc(uint8_t* data, size_t size)
|
|||
return crc;
|
||||
}
|
||||
|
||||
bool onewire_master_function_read(uint8_t function, uint8_t* data, size_t bits)
|
||||
bool onewire_master_function_read(uint8_t function, uint8_t* data, uint32_t bits)
|
||||
{
|
||||
// send function command
|
||||
buffer_size = 8; // function command is only one byte
|
||||
if (!(buffer=realloc(buffer, (buffer_size-1)/8+1))) { // allocate memory
|
||||
return false; // error in memory allocation
|
||||
}
|
||||
buffer[0] = function; // set function command
|
||||
buffer = &function; // set the buffer to the function code
|
||||
if (!onewire_master_write()) { // send command
|
||||
return false; // an error occurred
|
||||
}
|
||||
|
@ -222,52 +251,19 @@ bool onewire_master_function_read(uint8_t function, uint8_t* data, size_t bits)
|
|||
|
||||
// read data
|
||||
buffer_size = bits; // save number of bits to read
|
||||
buffer = data; // set the buffer to the data to write
|
||||
if (!onewire_master_read()) { // read bits from slave
|
||||
return false; // an error occurred
|
||||
}
|
||||
|
||||
// copy data to user buffer
|
||||
for (uint8_t i=0; i<bits/8; i++) { // copy bytes
|
||||
data[i] = buffer[i]; // copy data
|
||||
}
|
||||
// copy remaining bits
|
||||
switch (bits%8) {
|
||||
case 1:
|
||||
data[(bits-1)/8] = data[(bits-1)/8]&0x01;
|
||||
break;
|
||||
case 2:
|
||||
data[(bits-1)/8] = data[(bits-1)/8]&0x03;
|
||||
break;
|
||||
case 3:
|
||||
data[(bits-1)/8] = data[(bits-1)/8]&0x07;
|
||||
break;
|
||||
case 4:
|
||||
data[(bits-1)/8] = data[(bits-1)/8]&0x0f;
|
||||
break;
|
||||
case 5:
|
||||
data[(bits-1)/8] = data[(bits-1)/8]&0x1f;
|
||||
break;
|
||||
case 6:
|
||||
data[(bits-1)/8] = data[(bits-1)/8]&0x3f;
|
||||
break;
|
||||
case 7:
|
||||
data[(bits-1)/8] = data[(bits-1)/8]&0x7f;
|
||||
break;
|
||||
case 0: // no bits remaining
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool onewire_master_function_write(uint8_t function, uint8_t* data, size_t bits)
|
||||
bool onewire_master_function_write(uint8_t function, uint8_t* data, uint32_t bits)
|
||||
{
|
||||
// send function command
|
||||
buffer_size = 8; // function command is only one byte
|
||||
if (!(buffer=realloc(buffer, (buffer_size-1)/8+1))) { // allocate memory
|
||||
return false; // error in memory allocation
|
||||
}
|
||||
buffer[0] = function; // set function command
|
||||
buffer = &function; // set the buffer to the function code
|
||||
if (!onewire_master_write()) { // send command
|
||||
return false; // an error occurred
|
||||
}
|
||||
|
@ -278,12 +274,7 @@ bool onewire_master_function_write(uint8_t function, uint8_t* data, size_t bits)
|
|||
|
||||
// copy data from user buffer
|
||||
buffer_size = bits; // save number of bits to write
|
||||
if (!(buffer=realloc(buffer, (buffer_size-1)/8+1))) { // allocate memory
|
||||
return false; // error in memory allocation
|
||||
}
|
||||
for (uint8_t i=0; i<(buffer_size-1)/8+1; i++) { // copy bytes
|
||||
buffer[i] = data[i]; // copy data
|
||||
}
|
||||
buffer = data; // set the buffer to the data to write
|
||||
// write data
|
||||
if (!onewire_master_write()) { // read bits from slave
|
||||
return false; // an error occurred
|
||||
|
@ -304,7 +295,7 @@ uint64_t onewire_master_rom_read(void)
|
|||
|
||||
// return ROM code
|
||||
uint64_t code = 0;
|
||||
for (size_t i=0; i<8; i++) {
|
||||
for (uint32_t i=0; i<8; i++) {
|
||||
code += (uint64_t)rom_code[i]<<(8*i); // add byte
|
||||
}
|
||||
|
||||
|
@ -315,6 +306,7 @@ bool onewire_master_rom_search(uint64_t* code, bool alarm)
|
|||
{
|
||||
static uint8_t conflict_last = 64; // on which bit has the conflict been detected (64 means there hasn't been)
|
||||
uint8_t conflict_current = 64; // to remember on which bit the last unknown conflict has been detected
|
||||
uint8_t bits[1] = {0}; // small buffer to store the bits used to search the ROM codes
|
||||
|
||||
// send SEARCH ROM command
|
||||
uint8_t command = 0xf0; // SEARCH ROM command
|
||||
|
@ -329,6 +321,7 @@ bool onewire_master_rom_search(uint64_t* code, bool alarm)
|
|||
*code = 0; // restart search codes
|
||||
}
|
||||
|
||||
buffer = bits; // buffer to read up to two bits
|
||||
for (uint8_t bit=0; bit<64; bit++) { // go through all 64 bits ROM code
|
||||
buffer_size = 2; // read two first bits to detect conflict
|
||||
if (!onewire_master_read()) { // read ROM ID from slave
|
||||
|
@ -353,9 +346,6 @@ bool onewire_master_rom_search(uint64_t* code, bool alarm)
|
|||
goto end; // an error has occurred
|
||||
}
|
||||
buffer_size = 1; // to send next bit
|
||||
if (!(buffer=realloc(buffer, 1))) { // allocate memory
|
||||
goto end; // an error has occurred
|
||||
}
|
||||
buffer[0] = (*code>>bit); // set bit to send
|
||||
if (!onewire_master_write()) { // send bit
|
||||
goto end; // an error has occurred
|
||||
|
@ -431,13 +421,19 @@ void TIM_ISR(ONEWIRE_MASTER_TIMER)(void)
|
|||
break;
|
||||
default: // unknown state for this stage
|
||||
timer_disable_counter(TIM(ONEWIRE_MASTER_TIMER)); // disable timer
|
||||
timer_disable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC1IE); // disable all compare interrupt
|
||||
timer_disable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC2IE); // disable all compare interrupt
|
||||
timer_disable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC3IE); // disable all compare interrupt
|
||||
timer_disable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC4IE); // disable all compare interrupt
|
||||
gpio_set(GPIO(ONEWIRE_MASTER_PORT),GPIO(ONEWIRE_MASTER_PIN)); // pull signal high (idle state)
|
||||
onewire_master_state = ONEWIRE_STATE_ERROR; // indicate error
|
||||
}
|
||||
} else if (timer_get_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC1IF)) { // compare event happened for master pull low end
|
||||
if (onewire_master_parasite && (ONEWIRE_STATE_ERROR==onewire_master_state || ONEWIRE_STATE_DONE==onewire_master_state)) {
|
||||
gpio_set_mode(GPIO(ONEWIRE_MASTER_PORT), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(ONEWIRE_MASTER_PIN)); // provide parasite power
|
||||
} else {
|
||||
gpio_set_mode(GPIO(ONEWIRE_MASTER_PORT), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO(ONEWIRE_MASTER_PIN)); // normal 1-Wire communication (only using external pull-up resistor)
|
||||
}
|
||||
} else if (timer_get_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC1IF)) { // compare event happened for master pull low end for read
|
||||
timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC1IF); // clear flag
|
||||
switch (onewire_master_state) {
|
||||
case ONEWIRE_STATE_MASTER_READ: // master has to read a bit
|
||||
|
@ -479,6 +475,9 @@ void TIM_ISR(ONEWIRE_MASTER_TIMER)(void)
|
|||
} else if (timer_get_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC3IF)) { // compare event happened for end to time slot
|
||||
timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC3IF); // clear flag
|
||||
gpio_set(GPIO(ONEWIRE_MASTER_PORT),GPIO(ONEWIRE_MASTER_PIN)); // pull signal high to end time slot
|
||||
if (onewire_master_parasite) { // provide power during recovery time
|
||||
gpio_set_mode(GPIO(ONEWIRE_MASTER_PORT), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(ONEWIRE_MASTER_PIN)); // provide parasite power
|
||||
}
|
||||
} else if (timer_get_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC4IF)) { // compare event happened for slave presence detection
|
||||
timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC4IF); // clear flag
|
||||
if (gpio_get(GPIO(ONEWIRE_MASTER_PORT),GPIO(ONEWIRE_MASTER_PIN))) { // check is a slave let its presence know by pulling low
|
||||
|
|
|
@ -13,16 +13,20 @@
|
|||
*
|
||||
*/
|
||||
/** library for 1-wire protocol as master (API)
|
||||
* @file onewire_master.c
|
||||
* @file onewire_master.h
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @date 2017
|
||||
* @note peripherals used: timer @ref onewire_master_timer, GPIO @ref onewire_master_gpio
|
||||
* @warning this library does not support parasite power mode
|
||||
* @note overdrive mode is not provided
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/** setup 1-wire peripheral */
|
||||
void onewire_master_setup(void);
|
||||
/** setup 1-wire peripheral
|
||||
* @param[in] parasite enable parasite power (provide power over 1-Wire line when not communicating)
|
||||
* @warning multiple masters and interrupts are prevented when parasite power is used
|
||||
* @param[in] recovery recovery time in us between timeslot, e.g. to ensure enough parasite power is provided (0 if not required)
|
||||
*/
|
||||
void onewire_master_setup(bool parasite, uint16_t recovery);
|
||||
/** send reset pulse
|
||||
* @return if slaves have indicated their presence
|
||||
*/
|
||||
|
@ -30,11 +34,11 @@ bool onewire_master_reset(void);
|
|||
/** compute CRC for 1-Wire
|
||||
* @note this CRC-8 uses normal polynomial 0x31, reverse polynomial 0x8C, start value 0x00
|
||||
* @param[in] data bytes on which to calculate CRC checksum on
|
||||
* @param[in] size number of bytes in data
|
||||
* @param[in] length number of bytes in data
|
||||
* @return computed CRC checksum
|
||||
*/
|
||||
uint8_t onewire_master_crc(uint8_t* data, size_t size);
|
||||
/** send READ ROM command
|
||||
uint8_t onewire_master_crc(uint8_t* data, uint32_t length);
|
||||
/** send READ ROM command and read ROM code response
|
||||
* @note user needs to send reset pulse before
|
||||
* @return ROM code read
|
||||
*/
|
||||
|
@ -65,7 +69,7 @@ bool onewire_master_rom_match(uint64_t code);
|
|||
* @param[in] bits number of bits to read (0 if only the function command should be sent)
|
||||
* @return if operation succeeded
|
||||
*/
|
||||
bool onewire_master_function_read(uint8_t function, uint8_t* data, size_t bits);
|
||||
bool onewire_master_function_read(uint8_t function, uint8_t* data, uint32_t bits);
|
||||
/** issue function and write data
|
||||
* @note user needs to send a ROM command before
|
||||
* @param[in] function function command to send
|
||||
|
@ -73,4 +77,4 @@ bool onewire_master_function_read(uint8_t function, uint8_t* data, size_t bits);
|
|||
* @param[in] bits number of bits to write (0 if only the function command should be sent)
|
||||
* @return if operation succeeded
|
||||
*/
|
||||
bool onewire_master_function_write(uint8_t function, uint8_t* data, size_t bits);
|
||||
bool onewire_master_function_write(uint8_t function, uint8_t* data, uint32_t bits);
|
||||
|
|
|
@ -0,0 +1,409 @@
|
|||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library for 1-wire protocol as master (code)
|
||||
* @file onewire_slave.c
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @date 2017
|
||||
* @note peripherals used: GPIO and timer @ref onewire_slave_timer, GPIO @ref onewire_slave_gpio
|
||||
* @note overdrive mode is not supported
|
||||
* @implements 1-Wire protocol description from Book of iButton Standards
|
||||
*/
|
||||
|
||||
/* standard libraries */
|
||||
#include <stdint.h> // standard integer types
|
||||
#include <stdbool.h> // boolean type
|
||||
#include <stddef.h> // NULL definition
|
||||
|
||||
/* STM32 (including CM3) libraries */
|
||||
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities
|
||||
#include <libopencm3/cm3/nvic.h> // interrupt handler
|
||||
#include <libopencm3/stm32/rcc.h> // real-time control clock library
|
||||
#include <libopencm3/stm32/gpio.h> // general purpose input output library
|
||||
#include <libopencm3/stm32/timer.h> // timer library
|
||||
#include <libopencm3/stm32/exti.h> // external interrupt library
|
||||
|
||||
/* own libraries */
|
||||
#include "global.h" // help macros
|
||||
#include "onewire_slave.h" // own definitions
|
||||
|
||||
/** @defgroup onewire_slave_timer timer used to measure 1-wire signal timing
|
||||
* @{
|
||||
*/
|
||||
#define ONEWIRE_SLAVE_TIMER 2 /**< timer ID */
|
||||
/** @} */
|
||||
|
||||
/** @defgroup onewire_slave_gpio GPIO used for 1-wire signal
|
||||
* @warning ensure no same pin number on other parts are used for external interrupts
|
||||
* @note external pull-up resistor on pin is required (< 5 kOhm), generally provided by the master
|
||||
* @{
|
||||
*/
|
||||
#define ONEWIRE_SLAVE_PORT A /**< GPIO port */
|
||||
#define ONEWIRE_SLAVE_PIN 4 /**< GPIO pin */
|
||||
/** @} */
|
||||
|
||||
/** state of 1-Wire communication */
|
||||
static volatile enum {
|
||||
ONEWIRE_STATE_IDLE, /**< no current communication */
|
||||
ONEWIRE_STATE_RESET, /**< reset pulse has been detected */
|
||||
ONEWIRE_STATE_WAIT_PRESENCE, /**< waiting before sending the presence pulse */
|
||||
ONEWIRE_STATE_PULSE_PRESENCE, /**< sending the presence pulse */
|
||||
ONEWIRE_STATE_ROM_COMMAND, /**< slave is reading ROM command bits */
|
||||
ONEWIRE_STATE_ROM_READ, /**< slave is sending ROM code in response to ROM command READ ROM */
|
||||
ONEWIRE_STATE_ROM_MATCH, /**< master is sending ROM code to select slave */
|
||||
ONEWIRE_STATE_ROM_SEARCH_TRUE, /**< master is searching ROM code, slave will send first bit (not negated) */
|
||||
ONEWIRE_STATE_ROM_SEARCH_FALSE, /**< master is searching ROM code, slave will send first bit (not negated) */
|
||||
ONEWIRE_STATE_ROM_SEARCH_SELECT, /**< master is searching ROM code, slave will read selected bit */
|
||||
ONEWIRE_STATE_FUNCTION_COMMAND, /**< slave is reading function command bits */
|
||||
ONEWIRE_STATE_FUNCTION_DATA, /**< waiting for user to provide data to transfer */
|
||||
ONEWIRE_STATE_FUNCTION_READ, /**< slave is reading bits */
|
||||
ONEWIRE_STATE_FUNCTION_WRITE, /**< slave is writing bits */
|
||||
ONEWIRE_MAX /** to count the number of possible states */
|
||||
} onewire_slave_state = ONEWIRE_STATE_IDLE;
|
||||
|
||||
static uint8_t onewire_slave_rom_code[8] = {0}; /**< slave ROM code */
|
||||
|
||||
volatile bool onewire_slave_function_code_received = false;
|
||||
volatile uint8_t onewire_slave_function_code = 0;
|
||||
volatile bool onewire_slave_transfer_complete = false;
|
||||
|
||||
static volatile uint8_t bits_buffer = 0; /**< buffer for the incoming bits (up to one byte) */
|
||||
static volatile uint32_t bits_bit = 0; /**< number of incoming bits */
|
||||
static volatile uint8_t* onewire_slave_transfer_data = NULL; /**< data to transfer (read or write) */
|
||||
static volatile uint32_t onewire_slave_transfer_bits = 0; /**< number of bits to transfer */
|
||||
|
||||
/** compute CRC for 1-Wire
|
||||
* @note this CRC-8 uses normal polynomial 0x31, reverse polynomial 0x8C, start value 0x00
|
||||
* @param[in] data bytes on which to calculate CRC checksum on
|
||||
* @param[in] length number of bytes in data
|
||||
* @return computed CRC checksum
|
||||
*/
|
||||
static uint8_t onewire_slave_crc(uint8_t* data, uint32_t length)
|
||||
{
|
||||
if (NULL==data || 0==length) { // check input
|
||||
return 0; // wrong input
|
||||
}
|
||||
|
||||
uint8_t crc = 0x00; // initial value
|
||||
for (uint8_t i=0; i<length; i++) { // go through every byte
|
||||
crc ^= data[i]; // XOR byte
|
||||
for (uint8_t b=0; b<8; b++) { // go through every bit
|
||||
if (crc&0x01) { // least significant bit is set (we are using the reverse way)
|
||||
crc = (crc>>1)^0x8C; // // shift to the right (for the next bit) and XOR with (reverse) polynomial
|
||||
} else {
|
||||
crc >>= 1; // just shift right (for the next bit)
|
||||
}
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
void onewire_slave_setup(uint8_t family, uint64_t serial)
|
||||
{
|
||||
// save ROM code (LSB first)
|
||||
onewire_slave_rom_code[0] = family;
|
||||
onewire_slave_rom_code[1] = serial >> 40;
|
||||
onewire_slave_rom_code[2] = serial >> 32;
|
||||
onewire_slave_rom_code[3] = serial >> 24;
|
||||
onewire_slave_rom_code[4] = serial >> 16;
|
||||
onewire_slave_rom_code[5] = serial >> 8;
|
||||
onewire_slave_rom_code[6] = serial >> 0;
|
||||
onewire_slave_rom_code[7] = onewire_slave_crc(onewire_slave_rom_code, 7); // calculate CRC
|
||||
|
||||
// setup timer to generate/measure signal timing
|
||||
rcc_periph_clock_enable(RCC_TIM(ONEWIRE_SLAVE_TIMER)); // enable clock for timer peripheral
|
||||
timer_reset(TIM(ONEWIRE_SLAVE_TIMER)); // reset timer state
|
||||
timer_set_mode(TIM(ONEWIRE_SLAVE_TIMER), TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // set timer mode, use undivided timer clock, edge alignment (simple count), and count up
|
||||
timer_set_prescaler(TIM(ONEWIRE_SLAVE_TIMER), 1-1); // don't use prescale since this 16 bits timer allows to wait > 480 us used for the reset pulse ( 1/(72E6/1/(2**16))=910us )
|
||||
|
||||
// use comparator to time signal (without using the output), starting at slot start
|
||||
timer_set_period(TIM(ONEWIRE_SLAVE_TIMER), 480*(rcc_ahb_frequency/1000000)-1-1300); // minimum time needed for a reset pulse (480 < Trst), plus hand tuning
|
||||
timer_set_oc_mode(TIM(ONEWIRE_SLAVE_TIMER), TIM_OC1, TIM_OCM_FROZEN);
|
||||
timer_set_oc_value(TIM(ONEWIRE_SLAVE_TIMER), TIM_OC1, 16*(rcc_ahb_frequency/1000000)-1); // time to wait before sending the presence pulse, after the rising edge of the reset pulse (15 < Tpdh < 60)
|
||||
timer_set_oc_mode(TIM(ONEWIRE_SLAVE_TIMER), TIM_OC2, TIM_OCM_FROZEN);
|
||||
timer_set_oc_value(TIM(ONEWIRE_SLAVE_TIMER), TIM_OC2, 45*(rcc_ahb_frequency/1000000)-1-350); // time to sample the bit after being set (1 < Tlow1 < 15, 60 < Tslot < 120), or stop sending the bit use compare function to detect slave presence (15 = Trdv + 0 < Trelease < 45), plus hand tuning
|
||||
timer_set_oc_value(TIM(ONEWIRE_SLAVE_TIMER), TIM_OC3, 90*(rcc_ahb_frequency/1000000)-1); // time to stop the presence pulse (60 < Tpdl < 120)
|
||||
timer_clear_flag(TIM(ONEWIRE_SLAVE_TIMER), TIM_SR_UIF | TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF); // clear all interrupt flags
|
||||
timer_update_on_overflow(TIM(ONEWIRE_SLAVE_TIMER)); // only use counter overflow as UEV source (use overflow as start time or timeout)
|
||||
timer_enable_irq(TIM(ONEWIRE_SLAVE_TIMER), TIM_DIER_UIE); // enable update interrupt for overflow
|
||||
nvic_enable_irq(NVIC_TIM_IRQ(ONEWIRE_SLAVE_TIMER)); // catch interrupt in service routine
|
||||
|
||||
onewire_slave_function_code_received = false; // reset state
|
||||
onewire_slave_state = ONEWIRE_STATE_IDLE; // reset state
|
||||
onewire_slave_transfer_complete = false; // reset state
|
||||
|
||||
// setup GPIO with external interrupt
|
||||
rcc_periph_clock_enable(RCC_GPIO(ONEWIRE_SLAVE_PORT)); // enable clock for GPIO peripheral
|
||||
gpio_set(GPIO(ONEWIRE_SLAVE_PORT), GPIO(ONEWIRE_SLAVE_PIN)); // idle is high (using pull-up resistor)
|
||||
gpio_set_mode(GPIO(ONEWIRE_SLAVE_PORT), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO(ONEWIRE_SLAVE_PIN)); // control output using open drain (this mode also allows to read the input signal)
|
||||
rcc_periph_clock_enable(RCC_AFIO); // enable alternate function clock for external interrupt
|
||||
exti_select_source(EXTI(ONEWIRE_SLAVE_PIN), GPIO(ONEWIRE_SLAVE_PORT)); // mask external interrupt of this pin only for this port
|
||||
exti_set_trigger(EXTI(ONEWIRE_SLAVE_PIN), EXTI_TRIGGER_BOTH); // trigger on signal change
|
||||
exti_enable_request(EXTI(ONEWIRE_SLAVE_PIN)); // enable external interrupt
|
||||
nvic_enable_irq(NVIC_EXTI_IRQ(ONEWIRE_SLAVE_PIN)); // enable interrupt
|
||||
}
|
||||
|
||||
bool onewire_slave_function_read(uint8_t* data, size_t size)
|
||||
{
|
||||
if (NULL==data || 0==size) { // verify input
|
||||
return false;
|
||||
}
|
||||
if (UINT32_MAX/8<size) { // too many bits to transfer
|
||||
return false;
|
||||
}
|
||||
if (onewire_slave_state!=ONEWIRE_STATE_FUNCTION_DATA) { // not in the right state to transfer data
|
||||
return false;
|
||||
}
|
||||
onewire_slave_transfer_data = data; // save buffer to write to
|
||||
onewire_slave_transfer_bits = size*8; // number of bits to read
|
||||
onewire_slave_transfer_complete = false; // reset state
|
||||
bits_bit = 0; // reset number of bits read
|
||||
onewire_slave_state = ONEWIRE_STATE_FUNCTION_READ; // read data
|
||||
return true;
|
||||
}
|
||||
|
||||
bool onewire_slave_function_write(const uint8_t* data, size_t size)
|
||||
{
|
||||
if (NULL==data || 0==size) { // verify input
|
||||
return false;
|
||||
}
|
||||
if (onewire_slave_state!=ONEWIRE_STATE_FUNCTION_DATA) { // not in the right state to transfer data
|
||||
return false;
|
||||
}
|
||||
if (UINT32_MAX/8<size) { // too many bits to transfer
|
||||
return false;
|
||||
}
|
||||
onewire_slave_transfer_data = (uint8_t*)data; // save buffer to read from
|
||||
onewire_slave_transfer_bits = size*8; // number of bits to write
|
||||
onewire_slave_transfer_complete = false; // reset state
|
||||
bits_bit = 0; // reset number of bits written
|
||||
bits_buffer = onewire_slave_transfer_data[0]; // prepare byte to write
|
||||
onewire_slave_state = ONEWIRE_STATE_FUNCTION_WRITE; // write data
|
||||
return true;
|
||||
}
|
||||
|
||||
/** interrupt service routine called when 1-Wire signal changes */
|
||||
void EXTI_ISR(ONEWIRE_SLAVE_PIN)(void)
|
||||
{
|
||||
exti_reset_request(EXTI(ONEWIRE_SLAVE_PIN)); // reset interrupt
|
||||
if (gpio_get(GPIO(ONEWIRE_SLAVE_PORT), GPIO(ONEWIRE_SLAVE_PIN))) { // it's a rising edge
|
||||
switch (onewire_slave_state) {
|
||||
case ONEWIRE_STATE_RESET: // reset pulse has ended
|
||||
timer_disable_counter(TIM(ONEWIRE_SLAVE_TIMER)); // stop timer for reconfiguration
|
||||
timer_set_counter(TIM(ONEWIRE_SLAVE_TIMER), 0); // reset timer counter
|
||||
timer_clear_flag(TIM(ONEWIRE_SLAVE_TIMER), TIM_SR_CC1IF); // clear flag
|
||||
timer_enable_irq(TIM(ONEWIRE_SLAVE_TIMER), TIM_DIER_CC1IE); // enable compare interrupt for presence pulse
|
||||
timer_enable_counter(TIM(ONEWIRE_SLAVE_TIMER)); // start timer to generate timing
|
||||
onewire_slave_state = ONEWIRE_STATE_WAIT_PRESENCE; // set new stated
|
||||
break;
|
||||
case ONEWIRE_STATE_PULSE_PRESENCE: // we stopped sending the presence pulse
|
||||
onewire_slave_state = ONEWIRE_STATE_ROM_COMMAND; // we now expect a ROM command
|
||||
bits_bit = 0; // reset buffer bit count
|
||||
break; // no need to stop the time, the reset will be checked correctly
|
||||
default: // rising edge is not important is the other cases
|
||||
break; // nothing to do
|
||||
}
|
||||
} else { // it's a falling edge, the beginning of a new signal
|
||||
timer_disable_counter(TIM(ONEWIRE_SLAVE_TIMER)); // stop timer for reconfiguration
|
||||
timer_set_counter(TIM(ONEWIRE_SLAVE_TIMER), 0); // reset timer counter
|
||||
timer_disable_irq(TIM(ONEWIRE_SLAVE_TIMER), TIM_DIER_CC1IE | TIM_DIER_CC2IE | TIM_DIER_CC3IE); // disable all timers
|
||||
switch (onewire_slave_state) {
|
||||
case ONEWIRE_STATE_PULSE_PRESENCE: // we started sending the presence pulse
|
||||
timer_enable_irq(TIM(ONEWIRE_SLAVE_TIMER), TIM_DIER_CC3IE); // enable timer for end of pulse
|
||||
break;
|
||||
case ONEWIRE_STATE_ROM_COMMAND: // read ROM command bits
|
||||
case ONEWIRE_STATE_ROM_MATCH: // read ROM code bits
|
||||
case ONEWIRE_STATE_FUNCTION_COMMAND: // read function command bits
|
||||
case ONEWIRE_STATE_ROM_SEARCH_SELECT: // read selected ROM code bit
|
||||
case ONEWIRE_STATE_FUNCTION_READ: // read function data bit
|
||||
timer_enable_irq(TIM(ONEWIRE_SLAVE_TIMER), TIM_DIER_CC2IE); // enable timer for reading bit
|
||||
break;
|
||||
case ONEWIRE_STATE_ROM_READ: // send ROM code bit
|
||||
case ONEWIRE_STATE_ROM_SEARCH_TRUE: // send ROM code bit while searching ROM, not negated
|
||||
case ONEWIRE_STATE_ROM_SEARCH_FALSE: // send ROM code bit while searching ROM, already negated
|
||||
case ONEWIRE_STATE_FUNCTION_WRITE: // write function data bit
|
||||
timer_enable_irq(TIM(ONEWIRE_SLAVE_TIMER), TIM_DIER_CC2IE); // enable timer for reading bit
|
||||
if (0==(bits_buffer&(1<<(bits_bit%8)))) { // need to send a 0 bit
|
||||
gpio_clear(GPIO(ONEWIRE_SLAVE_PORT), GPIO(ONEWIRE_SLAVE_PIN)); // hold low to send 0 bit
|
||||
}
|
||||
break;
|
||||
case ONEWIRE_STATE_IDLE: // we only expect a reset
|
||||
default: // we don't expect any falling edge in other states
|
||||
onewire_slave_state = ONEWIRE_STATE_IDLE; // unexpected signal, reset to idle state
|
||||
break; // the timer overflow will confirm detect reset pulses
|
||||
}
|
||||
timer_clear_flag(TIM(ONEWIRE_SLAVE_TIMER), TIM_SR_UIF | TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF); // clear all flags
|
||||
timer_enable_counter(TIM(ONEWIRE_SLAVE_TIMER)); // start timer to measure the configured timeouts
|
||||
}
|
||||
}
|
||||
|
||||
/** interrupt service routine called for timer */
|
||||
void TIM_ISR(ONEWIRE_SLAVE_TIMER)(void)
|
||||
{
|
||||
if (timer_interrupt_source(TIM(ONEWIRE_SLAVE_TIMER), TIM_SR_UIF)) { // reset timer triggered, verify if it's a reset
|
||||
if (0==gpio_get(GPIO(ONEWIRE_SLAVE_PORT), GPIO(ONEWIRE_SLAVE_PIN))) { // signal it still low, thus it must be a reset
|
||||
onewire_slave_state = ONEWIRE_STATE_RESET; // update state
|
||||
}
|
||||
timer_disable_counter(TIM(ONEWIRE_SLAVE_TIMER)); // stop timer since there is nothing more to measure
|
||||
timer_disable_irq(TIM(ONEWIRE_SLAVE_TIMER), TIM_DIER_CC1IE | TIM_DIER_CC2IE | TIM_DIER_CC3IE); // disable all timers
|
||||
timer_clear_flag(TIM(ONEWIRE_SLAVE_TIMER), TIM_SR_UIF | TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF); // clear all flag (I have no idea why the others are get too, even when the interrupt is not enabled)
|
||||
}
|
||||
if (timer_interrupt_source(TIM(ONEWIRE_SLAVE_TIMER), TIM_SR_CC1IF)) { // wait for presence pulse timer triggered
|
||||
timer_clear_flag(TIM(ONEWIRE_SLAVE_TIMER), TIM_SR_CC1IF); // clear flag
|
||||
if (ONEWIRE_STATE_WAIT_PRESENCE==onewire_slave_state) { // we can now send the pulse
|
||||
onewire_slave_state = ONEWIRE_STATE_PULSE_PRESENCE; // save new state
|
||||
gpio_clear(GPIO(ONEWIRE_SLAVE_PORT), GPIO(ONEWIRE_SLAVE_PIN)); // send presence pulse (will also trigger the timer start)
|
||||
}
|
||||
}
|
||||
if (timer_interrupt_source(TIM(ONEWIRE_SLAVE_TIMER), TIM_SR_CC2IF)) { // time to read the bit, or stop writing it
|
||||
// read/write bit depending on bit
|
||||
switch (onewire_slave_state) {
|
||||
case ONEWIRE_STATE_ROM_COMMAND: // read ROM command code bit
|
||||
case ONEWIRE_STATE_ROM_MATCH: // read ROM code
|
||||
case ONEWIRE_STATE_FUNCTION_COMMAND: // read function command code bit
|
||||
case ONEWIRE_STATE_ROM_SEARCH_SELECT: // read selected ROM code bit
|
||||
case ONEWIRE_STATE_FUNCTION_READ: // read function data bit
|
||||
if (gpio_get(GPIO(ONEWIRE_SLAVE_PORT), GPIO(ONEWIRE_SLAVE_PIN))) { // bit is set to 1
|
||||
bits_buffer |= (1<<(bits_bit%8)); // set bit
|
||||
} else { // bit is set to 0
|
||||
bits_buffer &= ~(1<<(bits_bit%8)); // clear bit
|
||||
}
|
||||
bits_bit++; // go to next bit
|
||||
break;
|
||||
case ONEWIRE_STATE_ROM_READ: // write ROM code
|
||||
case ONEWIRE_STATE_FUNCTION_WRITE: // write function data bit
|
||||
gpio_set(GPIO(ONEWIRE_SLAVE_PORT), GPIO(ONEWIRE_SLAVE_PIN)); // stop sending bit
|
||||
bits_bit++; // go to next bit
|
||||
break;
|
||||
case ONEWIRE_STATE_ROM_SEARCH_TRUE: // ROM code bit is sent
|
||||
case ONEWIRE_STATE_ROM_SEARCH_FALSE: // ROM code bit is sent
|
||||
gpio_set(GPIO(ONEWIRE_SLAVE_PORT), GPIO(ONEWIRE_SLAVE_PIN)); // stop sending bit
|
||||
break;
|
||||
default: // these states don't need read/write
|
||||
break;
|
||||
}
|
||||
static uint8_t rom_code_byte; // which byte of the ROM code is processed
|
||||
// act on bit count
|
||||
switch (onewire_slave_state) {
|
||||
case ONEWIRE_STATE_ROM_COMMAND: // read ROM command
|
||||
if (bits_bit>7) { // complete ROM command code received
|
||||
bits_bit = 0; // reset buffer
|
||||
rom_code_byte = 0; // reset ROM code byte index
|
||||
switch (bits_buffer) { // act depending on ROM command code
|
||||
case 0x33: // READ ROM
|
||||
bits_buffer = onewire_slave_rom_code[rom_code_byte]; // prepare to send the first byte
|
||||
onewire_slave_state = ONEWIRE_STATE_ROM_READ; // write ROM code
|
||||
break;
|
||||
case 0xcc: // SKIP ROM
|
||||
onewire_slave_state = ONEWIRE_STATE_FUNCTION_COMMAND; // now read function command code
|
||||
break;
|
||||
case 0x55: // MATCH ROM
|
||||
onewire_slave_state = ONEWIRE_STATE_ROM_MATCH; // read ROM code
|
||||
break;
|
||||
case 0xf0: // SEARCH ROM
|
||||
bits_buffer = onewire_slave_rom_code[rom_code_byte]; // prepare to search code
|
||||
onewire_slave_state = ONEWIRE_STATE_ROM_SEARCH_TRUE; // prepare to start sending first new bit
|
||||
break;
|
||||
default: // unknown ROM code
|
||||
onewire_slave_state = ONEWIRE_STATE_IDLE; // go back to default idle state
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ONEWIRE_STATE_ROM_READ: // send ROM code
|
||||
if (bits_bit>7) { // complete byte transmitted
|
||||
rom_code_byte++; // go to next ROM code byte
|
||||
if (rom_code_byte>LENGTH(onewire_slave_rom_code)) { // complete ROM code send
|
||||
onewire_slave_state = ONEWIRE_STATE_IDLE; // go back to default idle state
|
||||
} else {
|
||||
bits_bit = 0; // reset buffer
|
||||
bits_buffer = onewire_slave_rom_code[rom_code_byte]; // send next ROM code byte
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ONEWIRE_STATE_ROM_MATCH: // compare ROM code
|
||||
if (bits_bit>7) { // complete byte received
|
||||
if (bits_buffer==onewire_slave_rom_code[rom_code_byte]) { // ROM code byte matches
|
||||
bits_bit = 0; // reset buffer
|
||||
rom_code_byte++; // go to next ROM code byte
|
||||
if (rom_code_byte>=LENGTH(onewire_slave_rom_code)) { // complete ROM code matches
|
||||
onewire_slave_state = ONEWIRE_STATE_FUNCTION_COMMAND; // now read function command code
|
||||
}
|
||||
} else { // ROM code does not match
|
||||
onewire_slave_state = ONEWIRE_STATE_IDLE; // stop comparing and go back to idle
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ONEWIRE_STATE_ROM_SEARCH_TRUE: // ROM code bit is send, prepare to send negated version
|
||||
bits_buffer ^= (1<<bits_bit); // negate bit
|
||||
onewire_slave_state = ONEWIRE_STATE_ROM_SEARCH_FALSE; // send negated version
|
||||
break;
|
||||
case ONEWIRE_STATE_ROM_SEARCH_FALSE: // negated ROM code bit is send, prepare to read selected bit
|
||||
onewire_slave_state = ONEWIRE_STATE_ROM_SEARCH_SELECT; // read selected
|
||||
break;
|
||||
case ONEWIRE_STATE_ROM_SEARCH_SELECT: // check if we are selected
|
||||
if ((bits_buffer&(1<<(bits_bit-1)))==(onewire_slave_rom_code[rom_code_byte]&(1<<(bits_bit-1)))) { // we have been selected
|
||||
onewire_slave_state = ONEWIRE_STATE_ROM_SEARCH_TRUE; // prepare to compare next bit
|
||||
} else { // we are no selected
|
||||
onewire_slave_state = ONEWIRE_STATE_IDLE; // go back to idle
|
||||
}
|
||||
if (bits_bit>7) { // complete byte searched
|
||||
bits_bit = 0; // reset buffer
|
||||
rom_code_byte++; // go to next ROM code byte
|
||||
if (rom_code_byte>=LENGTH(onewire_slave_rom_code)) { // complete ROM code search
|
||||
onewire_slave_state = ONEWIRE_STATE_FUNCTION_COMMAND; // now read function command code
|
||||
} else {
|
||||
bits_buffer = onewire_slave_rom_code[rom_code_byte]; // prepare next ROM code byte
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ONEWIRE_STATE_FUNCTION_COMMAND: // read function command
|
||||
if (bits_bit>7) { // complete function command code received
|
||||
onewire_slave_function_code = bits_buffer; // save function command code to user buffer
|
||||
onewire_slave_state = ONEWIRE_STATE_FUNCTION_DATA; // let the user transfer data
|
||||
onewire_slave_function_code_received = true; // notify user
|
||||
}
|
||||
break;
|
||||
case ONEWIRE_STATE_FUNCTION_READ: // save function data bit
|
||||
if (0==bits_bit%8) { // complete byte received
|
||||
onewire_slave_transfer_data[(bits_bit-1)/8] = bits_buffer; // save received bytes
|
||||
}
|
||||
if (bits_bit>=onewire_slave_transfer_bits) { // read transfer complete
|
||||
onewire_slave_transfer_data[(bits_bit-1)/8] = bits_buffer; // save last bits
|
||||
onewire_slave_state = ONEWIRE_STATE_FUNCTION_DATA; // let the user transfer more data
|
||||
onewire_slave_transfer_complete = true; // notify user
|
||||
}
|
||||
break;
|
||||
case ONEWIRE_STATE_FUNCTION_WRITE: // update function data bit to write
|
||||
if (0==bits_bit%8) { // complete byte transfer
|
||||
bits_buffer = onewire_slave_transfer_data[bits_bit/8]; // prepare next byte to write
|
||||
}
|
||||
if (bits_bit>=onewire_slave_transfer_bits) { // write transfer complete
|
||||
onewire_slave_state = ONEWIRE_STATE_FUNCTION_DATA; // let the user transfer more data
|
||||
onewire_slave_transfer_complete = true; // notify user
|
||||
}
|
||||
break;
|
||||
default: // no action needed
|
||||
break;
|
||||
}
|
||||
timer_clear_flag(TIM(ONEWIRE_SLAVE_TIMER), TIM_SR_CC2IF); // clear flag
|
||||
}
|
||||
if (timer_interrupt_source(TIM(ONEWIRE_SLAVE_TIMER), TIM_SR_CC3IF)) { // end of presence pulse timer triggered
|
||||
timer_clear_flag(TIM(ONEWIRE_SLAVE_TIMER), TIM_SR_CC3IF); // clear flag
|
||||
if (ONEWIRE_STATE_PULSE_PRESENCE==onewire_slave_state) {
|
||||
gpio_set(GPIO(ONEWIRE_SLAVE_PORT), GPIO(ONEWIRE_SLAVE_PIN)); // stop sending presence pulse
|
||||
// if the pin stays low the reset timer will catch it
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** library for 1-wire protocol as slave (API)
|
||||
* @file onewire_slave.h
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @date 2017
|
||||
* @note peripherals used: timer @ref onewire_slave_timer, GPIO @ref onewire_slave_gpio
|
||||
* @note overdrive mode is not supported
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
/** set when a function command code has been received
|
||||
* @note needs to be cleared by user
|
||||
*/
|
||||
extern volatile bool onewire_slave_function_code_received;
|
||||
/** last function command code received */
|
||||
extern volatile uint8_t onewire_slave_function_code;
|
||||
/** set when data read/write transfer has been completed
|
||||
* @note needs to be cleared by user
|
||||
*/
|
||||
extern volatile bool onewire_slave_transfer_complete;
|
||||
|
||||
/** setup 1-wire peripheral
|
||||
* @param[in] family family code for slave ROM code (8 bits)
|
||||
* @param[in] serial serial number for slave ROM code (48 bits)
|
||||
*/
|
||||
void onewire_slave_setup(uint8_t family, uint64_t serial);
|
||||
/** read data from master
|
||||
* @param[out] data buffer to save read bits
|
||||
* @param[in] size number of bytes to read
|
||||
* @return if transfer initialization succeeded
|
||||
* @note onewire_slave_transfer_complete is set when transfer is completed
|
||||
*/
|
||||
bool onewire_slave_function_read(uint8_t* data, size_t size);
|
||||
/** write data to master
|
||||
* @param[in] data data to write
|
||||
* @param[in] size number of bytes to write
|
||||
* @return if transfer initialization succeeded
|
||||
* @note onewire_slave_transfer_complete is set when transfer is completed
|
||||
*/
|
||||
bool onewire_slave_function_write(const uint8_t* data, size_t size);
|
|
@ -13,7 +13,7 @@
|
|||
*
|
||||
*/
|
||||
/** printing utilities to replace the large printf from the standard library (API)
|
||||
* use % as format specifier prefix, followed by + to enforce sign of prefix, 0 and 0-9 for padding, and format specifier
|
||||
* @note use % as format specifier prefix, followed by + to enforce sign of prefix, 0 and 0-9 for padding, and format specifier
|
||||
* format specifier supported are: c for far, s for string, u for uint32_t, d for int32_t, U for uint64_t, D for int64_t, x for lower case hex up to uint32_t, X for upper case hex up to uint32_t, b for bits up to uint32_t
|
||||
* @file print.h
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
|
|
|
@ -37,7 +37,7 @@ bool sensor_ds18b20_only(void);
|
|||
*/
|
||||
bool sensor_ds18b20_list(uint64_t* code);
|
||||
/** start converting (e.g. measuring) temperature
|
||||
* @waning conversion time to wait before reading temperature depends on the resolution set (9 bits: 93.75ms, 10 bits: 187.5ms, 11 bits: 375ms, 12 bits: 950ms)
|
||||
* @warning conversion time to wait before reading temperature depends on the resolution set (9 bits: 93.75ms, 10 bits: 187.5ms, 11 bits: 375ms, 12 bits: 950ms)
|
||||
* @param[in] code ROM code of sensor to start conversion on (0 for all, if only DS18B20 sensors are on the bus)
|
||||
* @return if conversion started
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue