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 project
======= =======
@ -6,12 +6,20 @@ project
summary summary
------- -------
*describe project purpose* Maxim DS2432 1k-Bit Protected 1-Wire EEPROM with SHA-1 Engine based on the datasheet
technology 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 board
===== =====
@ -45,17 +53,18 @@ dependencies
The source code uses the [libopencm3](http://libopencm3.org/) library. The source code uses the [libopencm3](http://libopencm3.org/) library.
The projects is already a git submodules. 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 firmware
-------- --------
To compile the firmware run `make`. To compile the firmware run `rake`.
documentation documentation
------------- -------------
To generate doxygen documentation run `make doc`. To generate doxygen documentation run `rake doc`.
flash flash
----- -----
@ -63,23 +72,23 @@ flash
There are two firmware images: `bootloader` and `application`. There are two firmware images: `bootloader` and `application`.
The `bootloader` image allows to flash the `application` over USB using the DFU protocol. 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 `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). 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). The `bootlaoder` image will be flashed using SWD (Serial Wire Debug).
For that you need an SWD adapter. For that you need an SWD adapter.
The `Makefile` uses a Black Magic Probe (per default), or a ST-Link V2 along OpenOCD software. 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. 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 debug
----- -----
SWD also allows to debug the code running on the micro-controller using GDB. 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 USB
--- ---
@ -87,5 +96,5 @@ USB
The firmware offers serial communication over USART1 and USB (using the CDC ACM device class). 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. 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. 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/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
/** STM32F1 application example /** STM32F1 Maxim DS2432 (1k-Bit Protected 1-Wire EEPROM with SHA-1 Engine) implementation
* @file main.c * @file application.c
* @author King Kévin <kingkevin@cuvoodoo.info> * @author King Kévin <kingkevin@cuvoodoo.info>
* @date 2016-2017 * @date 2016-2017
*/ */
@ -41,6 +41,7 @@
#include "print.h" // printing utilities #include "print.h" // printing utilities
#include "usart.h" // USART utilities #include "usart.h" // USART utilities
#include "usb_cdcacm.h" // USB CDC ACM utilities #include "usb_cdcacm.h" // USB CDC ACM utilities
#include "onewire_slave.h" // 1-Wire utilities
#define WATCHDOG_PERIOD 10000 /**< watchdog period in ms */ #define WATCHDOG_PERIOD 10000 /**< watchdog period in ms */
@ -143,14 +144,14 @@ static void process_command(char* str)
if (!word) { if (!word) {
time_rtc = rtc_get_counter_val(); // get time from internal RTC time_rtc = rtc_get_counter_val(); // get time from internal RTC
time_tm = localtime(&time_rtc); // convert time 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') { } 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; goto error;
} else { } else {
time_rtc = rtc_get_counter_val(); // get time from internal RTC time_rtc = rtc_get_counter_val(); // get time from internal RTC
time_tm = localtime(&time_rtc); // convert time 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_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_tm->tm_mday = (word[8]-'0')*10+(word[9]-'0')*1; // set day
time_rtc = mktime(time_tm); // get back seconds time_rtc = mktime(time_tm); // get back seconds
rtc_set_counter_val(time_rtc); // save time to internal RTC rtc_set_counter_val(time_rtc); // save time to internal RTC
@ -166,6 +167,138 @@ error:
return; 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 /** program entry point
* this is the firmware function started by the micro-controller * 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 rcc_clock_setup_in_hse_8mhz_out_72mhz(); // use 8 MHz high speed external clock to generate 72 MHz internal clock
#if DEBUG #if DEBUG
// enable functionalities for easier debug // enable functionalities for easier debug
DBGMCU_CR |= DBGMCU_CR_IWDG_STOP; // stop independent watchdog counter when code is halted 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 board_setup(); // setup board
usart_setup(); // setup USART (for printing) usart_setup(); // setup USART (for printing)
usb_cdcacm_setup(); // setup USB CDC ACM (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) #if !(DEBUG)
// show watchdog information // show watchdog information
@ -214,40 +346,68 @@ void main(void)
time_rtc= rtc_get_counter_val(); // get time from internal RTC time_rtc= rtc_get_counter_val(); // get time from internal RTC
time_tm = localtime(&time_rtc); // convert time 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 // main loop
printf("command input: ready\n"); printf("command input: ready\n");
bool action = false; // if an action has been performed don't go to sleep bool action = false; // if an action has been performed don't go to sleep
button_flag = false; // reset button flag 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 bool char_flag = false; // a new character has been received
while (true) { // infinite loop while (true) { // infinite loop
iwdg_reset(); // kick the dog iwdg_reset(); // kick the dog
while (usart_received) { // data received over UART while (usart_received) { // data received over UART
action = true; // action has been performed action = true; // action has been performed
led_toggle(); // toggle LED led_toggle(); // toggle LED
c = usart_getchar(); // store receive character ch = usart_getchar(); // store receive character
char_flag = true; // notify character has been received char_flag = true; // notify character has been received
} }
while (usb_cdcacm_received) { // data received over USB while (usb_cdcacm_received) { // data received over USB
action = true; // action has been performed action = true; // action has been performed
led_toggle(); // toggle LED 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 char_flag = true; // notify character has been received
} }
while (char_flag) { // user data received while (char_flag) { // user data received
char_flag = false; // reset flag char_flag = false; // reset flag
action = true; // action has been performed action = true; // action has been performed
printf("%c",c); // echo receive character printf("%c",ch); // echo receive character
if (c=='\r' || c=='\n') { // end of command received if (ch=='\r' || ch=='\n') { // end of command received
if (command_i>0) { // there is a command to process if (command_i>0) { // there is a command to process
command[command_i] = 0; // end string command[command_i] = 0; // end string
command_i = 0; // prepare for next command command_i = 0; // prepare for next command
process_command(command); // process user command process_command(command); // process user command
} }
} else { // user command input } 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 if (command_i<LENGTH(command)-2) { // verify if there is place to save next character
command_i++; // save next character command_i++; // save next character
} }
@ -255,18 +415,15 @@ void main(void)
} }
while (button_flag) { // user pressed button while (button_flag) { // user pressed button
action = true; // action has been performed action = true; // action has been performed
printf("button pressed\n");
led_toggle(); // toggle LED led_toggle(); // toggle LED
for (uint32_t i=0; i<1000000; i++) { // wait a bit to remove noise and double trigger printf("button pressed\n");
__asm__("nop");
}
button_flag = false; // reset flag button_flag = false; // reset flag
} }
while (rtc_internal_tick_flag) { // the internal RTC ticked while (rtc_internal_tick_flag) { // the internal RTC ticked
rtc_internal_tick_flag = false; // reset flag rtc_internal_tick_flag = false; // reset flag
action = true; // action has been performed 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 #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 #endif
time_rtc = rtc_get_counter_val(); // get time from internal RTC (seconds since Unix Epoch) 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) 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); 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 if (action) { // go to sleep if nothing had to be done, else recheck for activity
action = false; action = false;
} else { } else {

View File

@ -1,12 +1,12 @@
/* linker script for application running on STM32F103x8 micro-controller /* 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 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. */ /* Define memory regions. */
MEMORY MEMORY
{ {
rom (rx) : ORIGIN = 0x08000000 + 9K, LENGTH = 55K rom (rx) : ORIGIN = 0x08000000 + 8K, LENGTH = 56K
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
} }
PROVIDE(__application_beginning = ORIGIN(rom)); 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 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) 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) __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 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 /* 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 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. */ /* Define memory regions. */
MEMORY MEMORY
{ {
rom (rx) : ORIGIN = 0x08000000, LENGTH = 9K rom (rx) : ORIGIN = 0x08000000, LENGTH = 8K
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K
} }
PROVIDE(__application_beginning = ORIGIN(rom) + LENGTH(rom)); 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 rest of the definitions for the STM32F1 family */
INCLUDE libopencm3_stm32f1.ld INCLUDE libopencm3_stm32f1.ld

View File

@ -15,23 +15,26 @@
/** global definitions and methods (code) /** global definitions and methods (code)
* @file global.c * @file global.c
* @author King Kévin <kingkevin@cuvoodoo.info> * @author King Kévin <kingkevin@cuvoodoo.info>
* @date 2016 * @date 2016-2017
*/ */
/* standard libraries */ /* standard libraries */
#include <stdint.h> // standard integer types #include <stdint.h> // standard integer types
#include <stdlib.h> // general utilities #include <stdlib.h> // general utilities
/* STM32 (including CM3) libraries */ /* 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/rcc.h> // real-time control clock library
#include <libopencm3/stm32/gpio.h> // general purpose input output library #include <libopencm3/stm32/gpio.h> // general purpose input output library
#include <libopencm3/stm32/timer.h> // timer library #include <libopencm3/stm32/timer.h> // timer library
#include <libopencm3/cm3/nvic.h> // interrupt handler
#include <libopencm3/stm32/exti.h> // external interrupt defines #include <libopencm3/stm32/exti.h> // external interrupt defines
#include "global.h" // common methods #include "global.h" // common methods
#include "string.h" // memory utilities #include "string.h" // memory utilities
volatile bool button_flag = false; 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) char* b2s(uint64_t binary, uint8_t rjust)
{ {
@ -79,6 +82,44 @@ void led_toggle(void)
gpio_toggle(GPIO(LED_PORT), GPIO(LED_PIN)); 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) void board_setup(void)
{ {
// setup LED // setup LED

View File

@ -362,6 +362,16 @@ void led_off(void);
/** toggle board LED */ /** toggle board LED */
void led_toggle(void); 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 */ /** setup board peripherals */
void board_setup(void); void board_setup(void);

View File

@ -13,7 +13,7 @@
* *
*/ */
/** library to communicate using I2C as master (code) /** library to communicate using I2C as master (code)
* @file i2c_master.h * @file i2c_master.c
* @author King Kévin <kingkevin@cuvoodoo.info> * @author King Kévin <kingkevin@cuvoodoo.info>
* @date 2017 * @date 2017
* @note peripherals used: I2C @ref i2c_master_i2c, timer @ref i2c_master_timer * @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> * @author King Kévin <kingkevin@cuvoodoo.info>
* @date 2017 * @date 2017
* @note peripherals used: timer @ref onewire_master_timer, GPIO @ref onewire_master_gpio * @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 */ /* standard libraries */
#include <stdint.h> // standard integer types #include <stdint.h> // standard integer types
#include <stdbool.h> // boolean type #include <stdbool.h> // boolean type
#include <stdlib.h> // memory utilities #include <stddef.h> // NULL definition
/* STM32 (including CM3) libraries */ /* STM32 (including CM3) libraries */
#include <libopencmsis/core_cm3.h> // Cortex M3 utilities #include <libopencmsis/core_cm3.h> // Cortex M3 utilities
@ -43,7 +44,7 @@
/** @} */ /** @} */
/** @defgroup onewire_master_gpio GPIO used for 1-wire signal /** @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 */ #define ONEWIRE_MASTER_PORT A /**< GPIO port */
@ -62,17 +63,25 @@ volatile enum {
ONEWIRE_MAX /** to count the number of possible states */ ONEWIRE_MAX /** to count the number of possible states */
} onewire_master_state = ONEWIRE_STATE_IDLE; } onewire_master_state = ONEWIRE_STATE_IDLE;
volatile bool slave_presence = false; /**< if slaves have been detected */ static volatile bool slave_presence = false; /**< if slaves have been detected */
uint8_t* buffer = NULL; /**< input/output buffer for read/write commands/functions */ static uint8_t* buffer = NULL; /**< input/output buffer for read/write commands/functions */
size_t buffer_size = 0; /**< size of buffer in bits */ static uint32_t buffer_size = 0; /**< size of buffer in bits */
volatile size_t buffer_bit = 0; /**< number of bits read/written */ 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 // setup GPIO with external interrupt
rcc_periph_clock_enable(RCC_GPIO(ONEWIRE_MASTER_PORT)); // enable clock for GPIO peripheral 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) 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 // setup timer to generate/measure signal timing
rcc_periph_clock_enable(RCC_TIM(ONEWIRE_MASTER_TIMER)); // enable clock for timer peripheral 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 ) 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 // 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_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)); // use compare function to read of write (1 < Trw < 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)); // 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_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, 120*(rcc_ahb_frequency/1000000)); // use compare function to detect slave presence (60 < Tpdl < 240) 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_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) 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 // prepare timer
timer_disable_counter(TIM(ONEWIRE_MASTER_TIMER)); // disable timer to reconfigure it timer_disable_counter(TIM(ONEWIRE_MASTER_TIMER)); // disable timer to reconfigure it
timer_set_counter(TIM(ONEWIRE_MASTER_TIMER),0); // reset counter 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 slave_presence = false; // reset state
onewire_master_state = ONEWIRE_STATE_MASTER_RESET; // set new 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 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 reset procedure completed
@ -119,7 +138,7 @@ bool onewire_master_reset(void)
} }
/** write bits on 1-Wire bus /** 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 * @return if write succeeded
*/ */
static bool onewire_master_write(void) static bool onewire_master_write(void)
@ -130,16 +149,23 @@ static bool onewire_master_write(void)
// prepare timer // prepare timer
timer_disable_counter(TIM(ONEWIRE_MASTER_TIMER)); // disable timer to reconfigure it timer_disable_counter(TIM(ONEWIRE_MASTER_TIMER)); // disable timer to reconfigure it
timer_set_counter(TIM(ONEWIRE_MASTER_TIMER),0); // reset counter 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_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_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_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 timer_enable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC3IE); // enable compare interrupt for end of time slow
// start writing // 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 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 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 __WFI(); // go to sleep
} }
if (ONEWIRE_STATE_ERROR==onewire_master_state) { // an error occurred 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 if (0==buffer_size) { // check input
return false; 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 buffer_bit = 0; // reset bit index
onewire_master_state = ONEWIRE_STATE_MASTER_READ; // set new state onewire_master_state = ONEWIRE_STATE_MASTER_READ; // set new state
// prepare timer // 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_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_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_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 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 timer_enable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC3IE); // enable compare interrupt for end of time slow
// start reading // 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 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 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 __WFI(); // go to sleep
} }
if (ONEWIRE_STATE_ERROR==onewire_master_state) { // an error occurred if (ONEWIRE_STATE_ERROR==onewire_master_state) { // an error occurred
@ -184,14 +216,14 @@ static bool onewire_master_read(void)
return true; 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 return 0; // wrong input
} }
uint8_t crc = 0x00; // initial value 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 crc ^= data[i]; // XOR byte
for (uint8_t b=0; b<8; b++) { // go through every bit 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) 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; 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 // send function command
buffer_size = 8; // function command is only one byte buffer_size = 8; // function command is only one byte
if (!(buffer=realloc(buffer, (buffer_size-1)/8+1))) { // allocate memory buffer = &function; // set the buffer to the function code
return false; // error in memory allocation
}
buffer[0] = function; // set function command
if (!onewire_master_write()) { // send command if (!onewire_master_write()) { // send command
return false; // an error occurred 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 // read data
buffer_size = bits; // save number of bits to read 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 if (!onewire_master_read()) { // read bits from slave
return false; // an error occurred 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; 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 // send function command
buffer_size = 8; // function command is only one byte buffer_size = 8; // function command is only one byte
if (!(buffer=realloc(buffer, (buffer_size-1)/8+1))) { // allocate memory buffer = &function; // set the buffer to the function code
return false; // error in memory allocation
}
buffer[0] = function; // set function command
if (!onewire_master_write()) { // send command if (!onewire_master_write()) { // send command
return false; // an error occurred 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 // copy data from user buffer
buffer_size = bits; // save number of bits to write buffer_size = bits; // save number of bits to write
if (!(buffer=realloc(buffer, (buffer_size-1)/8+1))) { // allocate memory buffer = data; // set the buffer to the data to write
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
}
// write data // write data
if (!onewire_master_write()) { // read bits from slave if (!onewire_master_write()) { // read bits from slave
return false; // an error occurred return false; // an error occurred
@ -304,7 +295,7 @@ uint64_t onewire_master_rom_read(void)
// return ROM code // return ROM code
uint64_t code = 0; 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 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) 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 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 // send SEARCH ROM command
uint8_t command = 0xf0; // 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 *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 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 buffer_size = 2; // read two first bits to detect conflict
if (!onewire_master_read()) { // read ROM ID from slave 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 goto end; // an error has occurred
} }
buffer_size = 1; // to send next bit 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 buffer[0] = (*code>>bit); // set bit to send
if (!onewire_master_write()) { // send bit if (!onewire_master_write()) { // send bit
goto end; // an error has occurred goto end; // an error has occurred
@ -431,13 +421,19 @@ void TIM_ISR(ONEWIRE_MASTER_TIMER)(void)
break; break;
default: // unknown state for this stage default: // unknown state for this stage
timer_disable_counter(TIM(ONEWIRE_MASTER_TIMER)); // disable timer 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_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_CC3IE); // disable all compare interrupt
timer_disable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC4IE); // 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) gpio_set(GPIO(ONEWIRE_MASTER_PORT),GPIO(ONEWIRE_MASTER_PIN)); // pull signal high (idle state)
onewire_master_state = ONEWIRE_STATE_ERROR; // indicate error 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 timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC1IF); // clear flag
switch (onewire_master_state) { switch (onewire_master_state) {
case ONEWIRE_STATE_MASTER_READ: // master has to read a bit 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 } 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 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 } 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 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 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) /** library for 1-wire protocol as master (API)
* @file onewire_master.c * @file onewire_master.h
* @author King Kévin <kingkevin@cuvoodoo.info> * @author King Kévin <kingkevin@cuvoodoo.info>
* @date 2017 * @date 2017
* @note peripherals used: timer @ref onewire_master_timer, GPIO @ref onewire_master_gpio * @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 #pragma once
/** setup 1-wire peripheral */ /** setup 1-wire peripheral
void onewire_master_setup(void); * @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 /** send reset pulse
* @return if slaves have indicated their presence * @return if slaves have indicated their presence
*/ */
@ -30,11 +34,11 @@ bool onewire_master_reset(void);
/** compute CRC for 1-Wire /** compute CRC for 1-Wire
* @note this CRC-8 uses normal polynomial 0x31, reverse polynomial 0x8C, start value 0x00 * @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] 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 * @return computed CRC checksum
*/ */
uint8_t onewire_master_crc(uint8_t* data, size_t size); uint8_t onewire_master_crc(uint8_t* data, uint32_t length);
/** send READ ROM command /** send READ ROM command and read ROM code response
* @note user needs to send reset pulse before * @note user needs to send reset pulse before
* @return ROM code read * @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) * @param[in] bits number of bits to read (0 if only the function command should be sent)
* @return if operation succeeded * @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 /** issue function and write data
* @note user needs to send a ROM command before * @note user needs to send a ROM command before
* @param[in] function function command to send * @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) * @param[in] bits number of bits to write (0 if only the function command should be sent)
* @return if operation succeeded * @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) /** 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 * 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 * @file print.h
* @author King Kévin <kingkevin@cuvoodoo.info> * @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); bool sensor_ds18b20_list(uint64_t* code);
/** start converting (e.g. measuring) temperature /** 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) * @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 * @return if conversion started
*/ */