Compare commits

...

21 Commits

Author SHA1 Message Date
King Kévin 9357342280 README: fix formating 2017-09-14 17:12:14 +02:00
King Kévin f1e4624c2b README: document DS2432 implementation 2017-08-07 23:21:34 +02:00
King Kévin 73ac1db354 application: implement DS2432 (the reading part) 2017-08-07 23:20:45 +02:00
King Kévin f37b8309bb onewire_slave: use byte sizes instead of bits 2017-08-07 23:15:16 +02:00
King Kévin 84a3da0449 README: replace make with rake 2017-08-07 23:13:30 +02:00
King Kévin a1de197209 bootloader is now 8 kB thanks to better dependency handling 2017-08-06 09:53:46 +02:00
King Kévin 8a8424b349 replace Makefile with a Rakefile to better handle dependencies 2017-08-06 09:52:47 +02:00
King Kévin 5f4b03f6e1 onewire_slave: remove debug code 2017-08-04 07:43:05 +02:00
King Kévin 3c340a9157 application: example of 1-Wire slave usage 2017-08-03 20:50:11 +02:00
King Kévin 7fec0df419 add onewire slave library 2017-08-03 20:49:20 +02:00
King Kévin 8270f4e609 onewire_master: fix rom search 2017-08-02 15:20:01 +02:00
King Kévin a5e049d4fc doc: improve tag 2017-08-02 14:00:24 +02:00
King Kévin 17f92cd6c0 doc: fix typo 2017-08-02 13:57:36 +02:00
King Kévin fcded8a627 onewire_master: add parasite power support 2017-08-02 13:44:49 +02:00
King Kévin d1b546350b global: add ms in us sleep utilities 2017-08-02 13:44:16 +02:00
King Kévin 51bda8b90f onewire_master: remove malloc in favor of pre-allocated buffers 2017-08-01 19:17:22 +02:00
King Kévin 51dad418ad Makefile: use clang and GNU ld instead of ELLCC because it doesn't provide malloc 2017-08-01 19:16:09 +02:00
King Kévin 57f7e90cfd bootloader: fix clang warning 2017-08-01 19:13:56 +02:00
King Kévin b94c1d9fc3 sensor_ds18b20: fix doc typo 2017-08-01 19:13:27 +02:00
King Kévin bab0602d96 application: fix month handling 2017-08-01 11:49:43 +02:00
King Kévin 6e71958c03 Makefile: use core board as default 2017-08-01 11:49:06 +02:00
16 changed files with 1217 additions and 358 deletions

230
Makefile
View File

@ -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

View File

@ -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.

242
Rakefile Normal file
View File

@ -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

View File

@ -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 {

View File

@ -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));

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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,12 +118,13 @@ 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_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)
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
while (onewire_master_state!=ONEWIRE_STATE_DONE && onewire_master_state!=ONEWIRE_STATE_ERROR) { // wait until reset procedure completed
@ -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
@ -478,7 +474,10 @@ 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
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

View File

@ -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);

409
lib/onewire_slave.c Normal file
View File

@ -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
}
}
}

53
lib/onewire_slave.h Normal file
View File

@ -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);

View File

@ -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>

View File

@ -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
*/