diff options
Diffstat (limited to 'src/stm32f')
-rw-r--r-- | src/stm32f/.gitignore | 1 | ||||
l--------- | src/stm32f/FreeRTOS | 1 | ||||
-rw-r--r-- | src/stm32f/Makefile | 131 | ||||
-rw-r--r-- | src/stm32f/bin/.gitignore | 2 | ||||
l--------- | src/stm32f/bsp | 1 | ||||
-rw-r--r-- | src/stm32f/obj/.gitignore | 2 | ||||
-rw-r--r-- | src/stm32f/src/Core/FreeRTOSConfig.h | 3 | ||||
-rw-r--r-- | src/stm32f/src/Core/common.c | 7 | ||||
-rw-r--r-- | src/stm32f/src/Core/main.c | 74 | ||||
-rw-r--r-- | src/stm32f/src/Core/usart.c | 280 | ||||
l--------- | src/stm32f/tm_stm32f4_ds18b20.c | 1 | ||||
l--------- | src/stm32f/tm_stm32f4_ds18b20.h | 1 | ||||
l--------- | src/stm32f/tm_stm32f4_onewire.c | 1 | ||||
l--------- | src/stm32f/tm_stm32f4_onewire.h | 1 |
14 files changed, 506 insertions, 0 deletions
diff --git a/src/stm32f/.gitignore b/src/stm32f/.gitignore new file mode 100644 index 0000000..6533942 --- /dev/null +++ b/src/stm32f/.gitignore @@ -0,0 +1 @@ +vc.h diff --git a/src/stm32f/FreeRTOS b/src/stm32f/FreeRTOS new file mode 120000 index 0000000..51f4e96 --- /dev/null +++ b/src/stm32f/FreeRTOS @@ -0,0 +1 @@ +../FreeRTOS
\ No newline at end of file diff --git a/src/stm32f/Makefile b/src/stm32f/Makefile new file mode 100644 index 0000000..a26cdbd --- /dev/null +++ b/src/stm32f/Makefile @@ -0,0 +1,131 @@ +### +# GNU ARM Embedded Toolchain +CC=arm-none-eabi-gcc +LD=arm-none-eabi-ld +AR=arm-none-eabi-ar +AS=arm-none-eabi-as +CP=arm-none-eabi-objcopy +OD=arm-none-eabi-objdump +SIZE=arm-none-eabi-size + +### +# Directory Structure +BINDIR=bin +SRCDIR=. +ODIR=obj + +### +# Find source files +ASOURCES=$(shell find -L $(SRCDIR) -name '*.s') +CSOURCES+=$(shell find -L $(SRCDIR) -name '*.c') +# Find header directories +INC=$(shell find -L . -name '*.h' -exec dirname {} \; | uniq) +INCLUDES=$(INC:%=-I%) -I ../common/includes/ +# Create object list +OBJECTS=$(ASOURCES:%.s=%.o) +OBJECTS+=$(CSOURCES:%.c=obj/%.o) +# Define output files ELF & IHEX +BINELF=outp.elf +BINHEX=outp.hex + +### +# MCU FLAGS +MCFLAGS=-mcpu=cortex-m4 -mthumb -mlittle-endian \ +-mfpu=fpv4-sp-d16 -mfloat-abi=softfp -mthumb-interwork +# COMPILE FLAGS +DEFS=-DUSE_STDPERIPH_DRIVER -DSTM32F4XX -DARM_MATH_CM4 -D__FPU_PRESENT=1 +CFLAGS =-Wall -ggdb -std=c99 -c $(MCFLAGS) $(DEFS) $(INCLUDES) +# LINKER FLAGS +LDSCRIPT= bsp/stm32_flash.ld +LDFLAGS =-T $(LDSCRIPT) --specs=nosys.specs $(MCFLAGS) -Wl,-Map=$(BINDIR)/outp.map + +### +# Optimizations +OPT?='O2 O3 O6' +# O1 and O4 are irrelevant +# O5 breaks FreeRTOS somehow +# I'm not trusting O7 + +ifneq ($(filter O1,$(OPT)),) +CXXFLAGS+=-fno-exceptions # Uncomment to disable exception handling +DEFS+=-DNO_EXCEPTIONS # The source code has to comply with this rule +endif + +ifneq ($(filter O2,$(OPT)),) +CFLAGS+=-Os # Optimize for size https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html +CXXFLAGS+=-Os +LDFLAGS+=-Os # Optimize for size https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html +endif + +ifneq ($(filter O3,$(OPT)),) +CFLAGS+=-ffunction-sections -fdata-sections # Place each function or data item into its own section in the output file +CXXFLAGS+=-ffunction-sections -fdata-sections # -||- +LDFLAGS+=-Wl,-gc-sections # Remove isolated unused sections +endif + +ifneq ($(filter O4,$(OPT)),) +CFLAGS+=-fno-builtin # Disable C++ exception handling +CXXFLAGS+=-fno-builtin # Disable C++ exception handling +endif + +ifneq ($(filter O5,$(OPT)),) +CFLAGS+=-flto # Enable link time optimization +CXXFLAGS+=-flto # Enable link time optimization +LDFLAGS+=-flto # Enable link time optimization +endif + +ifneq ($(filter O6,$(OPT)),) +CXXFLAGS+=-fno-rtti # Disable type introspection +endif + +ifneq ($(findstring O7,$(OPT)),) +LDFLAGS+=--specs=nano.specs # Use size optimized newlib +endif + +### +# Build Rules +.PHONY: all release debug clean + +all: release + +release: $(BINDIR)/$(BINHEX) + +debug: CFLAGS+=-g +debug: LDFLAGS+=-g +debug: release + +$(BINDIR)/$(BINHEX): $(BINDIR)/$(BINELF) + $(CP) -O ihex $< $@ + +$(BINDIR)/$(BINELF): $(OBJECTS) vc.h + $(CC) $(LDFLAGS) $(OBJECTS) -o $@ + $(SIZE) $(BINDIR)/$(BINELF) + +dir_guard=@mkdir -p $(@D) + +obj/%.o: %.c $(INC) + $(dir_guard) + $(CC) $(CFLAGS) $< -o $@ + +obj/%.o: %.s + $(dir_guard) + $(CC) $(CFLAGS) $< -o $@ + +vc.h: ../../.git/logs/HEAD + echo "// This file is generated by Makefile." > vc.h + echo "// Do not edit this file!" >> vc.h + git log -1 --format="format:#define GIT_VERSION \"%h\"" >> vc.h + echo >> vc.h + echo >> vc.h + +clean: + rm -f $(OBJECTS) $(BINDIR)/$(BINELF) $(BINDIR)/$(BINHEX) + +# Connect to openocd's gdb server on port 3333 +deploy: $(BINDIR)/$(BINELF) +ifeq ($(wildcard /opt/openocd/bin/openocd),) + /usr/bin/openocd -f /usr/share/openocd/scripts/board/stm32f4discovery.cfg -c "program bin/"$(BINELF)" verify reset" -c "init" -c "reset" -c "exit" +else + /opt/openocd/bin/openocd -f /opt/openocd/share/openocd/scripts/board/stm32f4discovery.cfg -c "program bin/"$(BINELF)" verify reset" -c "init" -c "reset" -c "exit" +endif + diff --git a/src/stm32f/bin/.gitignore b/src/stm32f/bin/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/src/stm32f/bin/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/src/stm32f/bsp b/src/stm32f/bsp new file mode 120000 index 0000000..5d9120a --- /dev/null +++ b/src/stm32f/bsp @@ -0,0 +1 @@ +../bsp
\ No newline at end of file diff --git a/src/stm32f/obj/.gitignore b/src/stm32f/obj/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/src/stm32f/obj/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/src/stm32f/src/Core/FreeRTOSConfig.h b/src/stm32f/src/Core/FreeRTOSConfig.h new file mode 100644 index 0000000..8d6f128 --- /dev/null +++ b/src/stm32f/src/Core/FreeRTOSConfig.h @@ -0,0 +1,3 @@ +#include "../../../common/src/Core/FreeRTOSConfig.h" + +#define configCHECK_FOR_STACK_OVERFLOW 2 // Default: 2 diff --git a/src/stm32f/src/Core/common.c b/src/stm32f/src/Core/common.c new file mode 100644 index 0000000..a647d26 --- /dev/null +++ b/src/stm32f/src/Core/common.c @@ -0,0 +1,7 @@ +#include <stm32f4xx.h> + +#include "../../../common/src/Core/common.c" + +void hard_fault_handler_extra() { + usart_debug("SCB_SHCSR = %x\n", SCB->SHCSR); +} diff --git a/src/stm32f/src/Core/main.c b/src/stm32f/src/Core/main.c new file mode 100644 index 0000000..65d535e --- /dev/null +++ b/src/stm32f/src/Core/main.c @@ -0,0 +1,74 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016 Matthias P. Braendli, Maximilien Cuony + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +*/ + +#include "stm32f4xx_conf.h" + + +#define GPIOD_BOARD_LED_GREEN GPIO_Pin_12 +#define GPIOD_BOARD_LED_ORANGE GPIO_Pin_13 +#define GPIOD_BOARD_LED_RED GPIO_Pin_14 +#define GPIOD_BOARD_LED_BLUE GPIO_Pin_15 + +#include "../../../common/src/Core/main.c" + + +void init() { + /* Initialise the onboard peripherals + * Four LEDs and one push-button + */ + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); + + // Configure PD12, PD13, PD14 and PD15 in output pushpull mode + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.GPIO_Pin = + GPIOD_BOARD_LED_GREEN | + GPIOD_BOARD_LED_ORANGE | + GPIOD_BOARD_LED_RED | + GPIOD_BOARD_LED_BLUE; + + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; + GPIO_Init(GPIOD, &GPIO_InitStructure); + + // Init PushButton + GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; + GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; + GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; // TODO is there an external pullup ? + GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; + GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; + GPIO_Init(GPIOA, &GPIO_InitStructure); + + + /* Setup Watchdog + * The IWDG runs at 32kHz. With a prescaler of 32 -> 1kHz. + * Counting to 2000 / 1000 = 2 seconds + */ + IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); + IWDG_SetPrescaler(IWDG_Prescaler_32); + IWDG_SetReload(2000); + IWDG_Enable(); +} diff --git a/src/stm32f/src/Core/usart.c b/src/stm32f/src/Core/usart.c new file mode 100644 index 0000000..29c8a20 --- /dev/null +++ b/src/stm32f/src/Core/usart.c @@ -0,0 +1,280 @@ + +/* + * The MIT License (MIT) + * + * Copyright (c) 2016 Matthias P. Braendli + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. +*/ + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <inttypes.h> +#include "Core/common.h" +#include "Core/usart.h" +#include <stm32f4xx.h> +#include <stm32f4xx_usart.h> +#include <stm32f4xx_conf.h> +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +/* USART 3 on PD8 and PD9 + * See pio.txt for PIO allocation details */ +const uint16_t GPIOD_PIN_USART3_TX = GPIO_Pin_8; +const uint16_t GPIOD_PIN_USART3_RX = GPIO_Pin_9; + +/* USART 2 on PA2 and PA3 */ +const uint16_t GPIOA_PIN_USART2_RX = GPIO_Pin_3; +const uint16_t GPIOA_PIN_USART2_TX = GPIO_Pin_2; + +// The ISR writes into this buffer +static char nmea_sentence[MAX_NMEA_SENTENCE_LEN]; +static int nmea_sentence_last_written = 0; + +// Once a completed NMEA sentence is received in the ISR, +// it is appended to this queue +static QueueHandle_t usart_nmea_queue; + +void usart_init() +{ + // ============== PC DEBUG USART =========== + RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); + + GPIO_InitTypeDef GPIO_InitStruct; + GPIO_InitStruct.GPIO_Pin = GPIOA_PIN_USART2_RX | GPIOA_PIN_USART2_TX; + GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; + GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_Init(GPIOA, &GPIO_InitStruct); + + GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2); + GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2); + + // Setup USART2 for 9600,8,N,1 + USART_InitTypeDef USART_InitStruct; + USART_InitStruct.USART_BaudRate = 9600; + USART_InitStruct.USART_WordLength = USART_WordLength_8b; + USART_InitStruct.USART_StopBits = USART_StopBits_1; + USART_InitStruct.USART_Parity = USART_Parity_No; + USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; + USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; + USART_Init(USART2, &USART_InitStruct); + +#if USART2_RECEIVE_ENABLE + // enable the USART2 receive interrupt + USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); + + NVIC_InitTypeDef NVIC_InitStructure; + NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 6; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + + NVIC_SetPriority(USART2_IRQn, 6); +#endif + + // finally this enables the complete USART2 peripheral + USART_Cmd(USART2, ENABLE); +} + +void usart_gps_init() +{ + usart_nmea_queue = xQueueCreate(15, MAX_NMEA_SENTENCE_LEN); + if (usart_nmea_queue == 0) { + while(1); /* fatal error */ + } + + // ============== GPS USART =========== + // Setup GPIO D and connect to USART 3 + RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); + RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); + + GPIO_InitTypeDef GPIO_InitStruct; + GPIO_InitStruct.GPIO_Pin = GPIOD_PIN_USART3_RX | GPIOD_PIN_USART3_TX; + GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; + GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; + GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; + GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; + GPIO_Init(GPIOD, &GPIO_InitStruct); + + GPIO_PinAFConfig(GPIOD, GPIO_PinSource8, GPIO_AF_USART3); + GPIO_PinAFConfig(GPIOD, GPIO_PinSource9, GPIO_AF_USART3); + + // Setup USART3 for 9600,8,N,1 + USART_InitTypeDef USART_InitStruct; + USART_InitStruct.USART_BaudRate = 9600; + USART_InitStruct.USART_WordLength = USART_WordLength_8b; + USART_InitStruct.USART_StopBits = USART_StopBits_1; + USART_InitStruct.USART_Parity = USART_Parity_No; + USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; + USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; + USART_Init(USART3, &USART_InitStruct); + + + // enable the USART3 receive interrupt + USART_ITConfig(USART3, USART_IT_RXNE, ENABLE); + + NVIC_InitTypeDef NVIC_InitStructure; + NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 6; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; + NVIC_Init(&NVIC_InitStructure); + + NVIC_SetPriority(USART3_IRQn, 6); + + // finally this enables the complete USART3 peripheral + USART_Cmd(USART3, ENABLE); +} + +// Make sure Tasks are suspended when this is called! +static void usart_puts(USART_TypeDef* USART, const char* str) +{ + while(*str) { + // wait until data register is empty + USART_SendData(USART, *str); + while(USART_GetFlagStatus(USART, USART_FLAG_TXE) == RESET) ; + str++; + } +} + +void usart_gps_puts(const char* str) +{ + vTaskSuspendAll(); + return usart_puts(USART3, str); + xTaskResumeAll(); +} + +#define MAX_MSG_LEN 80 +static char usart_debug_message[MAX_MSG_LEN]; + +void usart_debug_timestamp() +{ + // Don't call printf here, to reduce stack usage + uint64_t now = timestamp_now(); + if (now == 0) { + usart_puts(USART2, "[0] "); + } + else { + char ts_str[64]; + int i = 63; + + ts_str[i--] = '\0'; + ts_str[i--] = ' '; + ts_str[i--] = ']'; + + while (now > 0 && i >= 0) { + ts_str[i--] = '0' + (now % 10); + now /= 10; + } + ts_str[i] = '['; + + usart_puts(USART2, &ts_str[i]); + } +} + +void usart_debug(const char *format, ...) +{ + va_list list; + va_start(list, format); + vsnprintf(usart_debug_message, MAX_MSG_LEN-1, format, list); + + vTaskSuspendAll(); + usart_debug_timestamp(); + usart_puts(USART2, usart_debug_message); + xTaskResumeAll(); + + va_end(list); +} + +void usart_debug_puts(const char* str) +{ + vTaskSuspendAll(); + usart_debug_timestamp(); + usart_puts(USART2, str); + xTaskResumeAll(); +} + +int usart_get_nmea_sentence(char* nmea) +{ + return xQueueReceive(usart_nmea_queue, nmea, portMAX_DELAY); +} + + +static void usart_clear_nmea_buffer(void) +{ + for (int i = 0; i < MAX_NMEA_SENTENCE_LEN; i++) { + nmea_sentence[i] = '\0'; + } + nmea_sentence_last_written = 0; +} + +void USART3_IRQHandler(void) +{ + if (USART_GetITStatus(USART3, USART_IT_RXNE)) { + char t = USART3->DR; + + if (nmea_sentence_last_written == 0) { + if (t == '$') { + // Likely new start of sentence + nmea_sentence[nmea_sentence_last_written] = t; + nmea_sentence_last_written++; + } + } + else if (nmea_sentence_last_written < MAX_NMEA_SENTENCE_LEN) { + nmea_sentence[nmea_sentence_last_written] = t; + nmea_sentence_last_written++; + + if (t == '\n') { + int success = xQueueSendToBackFromISR( + usart_nmea_queue, + nmea_sentence, + NULL); + + if (success == pdFALSE) { + trigger_fault(FAULT_SOURCE_USART); + } + + usart_clear_nmea_buffer(); + } + } + else { + // Buffer overrun without a meaningful NMEA message. + usart_clear_nmea_buffer(); + } + } +} + +void USART2_IRQHandler(void) +{ + if (USART_GetITStatus(USART2, USART_IT_RXNE)) { + char t = USART2->DR; + if (t == 'h') { + usart_debug_puts("help: no commands supported yet!\r\n"); + } + else { + usart_debug("Unknown command %c\r\n", t); + } + } +} diff --git a/src/stm32f/tm_stm32f4_ds18b20.c b/src/stm32f/tm_stm32f4_ds18b20.c new file mode 120000 index 0000000..1dca5f5 --- /dev/null +++ b/src/stm32f/tm_stm32f4_ds18b20.c @@ -0,0 +1 @@ +../ds18b20/tm_stm32f4_ds18b20.c
\ No newline at end of file diff --git a/src/stm32f/tm_stm32f4_ds18b20.h b/src/stm32f/tm_stm32f4_ds18b20.h new file mode 120000 index 0000000..6b8bae8 --- /dev/null +++ b/src/stm32f/tm_stm32f4_ds18b20.h @@ -0,0 +1 @@ +../ds18b20/tm_stm32f4_ds18b20.h
\ No newline at end of file diff --git a/src/stm32f/tm_stm32f4_onewire.c b/src/stm32f/tm_stm32f4_onewire.c new file mode 120000 index 0000000..21e6c82 --- /dev/null +++ b/src/stm32f/tm_stm32f4_onewire.c @@ -0,0 +1 @@ +../ds18b20/tm_stm32f4_onewire.c
\ No newline at end of file diff --git a/src/stm32f/tm_stm32f4_onewire.h b/src/stm32f/tm_stm32f4_onewire.h new file mode 120000 index 0000000..1779648 --- /dev/null +++ b/src/stm32f/tm_stm32f4_onewire.h @@ -0,0 +1 @@ +../ds18b20/tm_stm32f4_onewire.h
\ No newline at end of file |