Add basic thermal printer control
This commit is contained in:
commit
c695969e59
2
.gitignore
vendored
Executable file
2
.gitignore
vendored
Executable file
|
@ -0,0 +1,2 @@
|
||||||
|
build/*
|
||||||
|
src/.local.vimrc
|
29
.vscode/c_cpp_properties.json
vendored
Executable file
29
.vscode/c_cpp_properties.json
vendored
Executable file
|
@ -0,0 +1,29 @@
|
||||||
|
{
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "STM32F103x8",
|
||||||
|
"includePath": [
|
||||||
|
"/usr/lib/gcc/arm-none-eabi/7.3.0/include",
|
||||||
|
"/opt/stm32cube/STM32Cube_FW_F1_V1.6.0/Drivers/CMSIS/Include",
|
||||||
|
"/opt/stm32cube/STM32Cube_FW_F1_V1.6.0/Drivers/CMSIS/Device/ST/STM32F1xx/Include",
|
||||||
|
"${workspaceRoot}"
|
||||||
|
],
|
||||||
|
"defines": [
|
||||||
|
"_DEFAULT_SOURCE",
|
||||||
|
"STM32F103xB"
|
||||||
|
],
|
||||||
|
"intelliSenseMode": "clang-x64",
|
||||||
|
"browse": {
|
||||||
|
"path": [
|
||||||
|
"/usr/lib/gcc/arm-none-eabi/7.3.0/include",
|
||||||
|
"/opt/stm32cube/STM32Cube_FW_F1_V1.6.0/Drivers/CMSIS/Include",
|
||||||
|
"/opt/stm32cube/STM32Cube_FW_F1_V1.6.0/Drivers/CMSIS/Device/ST/STM32F1xx/Include",
|
||||||
|
"${workspaceRoot}"
|
||||||
|
],
|
||||||
|
"limitSymbolsToIncludedHeaders": true,
|
||||||
|
"databaseFilename": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": 4
|
||||||
|
}
|
73
.vscode/launch.json
vendored
Executable file
73
.vscode/launch.json
vendored
Executable file
|
@ -0,0 +1,73 @@
|
||||||
|
{
|
||||||
|
// Verwendet IntelliSense zum Ermitteln möglicher Attribute.
|
||||||
|
// Zeigen Sie auf vorhandene Attribute, um die zugehörigen Beschreibungen anzuzeigen.
|
||||||
|
// Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Debug STM32F103C8T6",
|
||||||
|
"type": "cppdbg",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceRoot}/build/main.elf",
|
||||||
|
"cwd": ".",
|
||||||
|
"MIMode": "gdb",
|
||||||
|
"miDebuggerServerAddress": "localhost:3333",
|
||||||
|
"preLaunchTask": "make",
|
||||||
|
"miDebuggerPath": "/usr/bin/arm-none-eabi-gdb",
|
||||||
|
"internalConsoleOptions": "openOnSessionStart",
|
||||||
|
"environment": [],
|
||||||
|
"setupCommands": [
|
||||||
|
{
|
||||||
|
"description": "Load ELF file into GDB",
|
||||||
|
"text": "file ${workspaceRoot}/build/main.elf",
|
||||||
|
"ignoreFailures": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Connect to remote",
|
||||||
|
"text": "target remote :3333",
|
||||||
|
"ignoreFailures": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Reset and Halt",
|
||||||
|
"text": "monitor reset halt",
|
||||||
|
"ignoreFailures": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Flash",
|
||||||
|
"text": "monitor flash write_image erase ${workspaceRoot}/build/main.bin 0x8000000",
|
||||||
|
"ignoreFailures": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Reset and Halt",
|
||||||
|
"text": "monitor reset halt",
|
||||||
|
"ignoreFailures": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Enable semihosting",
|
||||||
|
"text": "monitor arm semihosting enable",
|
||||||
|
"ignoreFailures": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"serverStarted": "watchpoints",
|
||||||
|
"debugServerPath": "/usr/bin/openocd",
|
||||||
|
"debugServerArgs": "-f interface/stlink-v2.cfg -f target/stm32f1x.cfg",
|
||||||
|
"customLaunchSetupCommands": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"stopAtEntry": true,
|
||||||
|
"logging": {
|
||||||
|
"exceptions": true,
|
||||||
|
"moduleLoad": true,
|
||||||
|
"programOutput": true,
|
||||||
|
"engineLogging": false,
|
||||||
|
"trace": false,
|
||||||
|
"traceResponse": false
|
||||||
|
},
|
||||||
|
"externalConsole": false,
|
||||||
|
"filterStderr": true,
|
||||||
|
"filterStdout": false,
|
||||||
|
"showDisplayString": true,
|
||||||
|
"targetArchitecture": "arm"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
14
.vscode/settings.json
vendored
Executable file
14
.vscode/settings.json
vendored
Executable file
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"files.associations": {
|
||||||
|
"usb_descriptors.h": "c",
|
||||||
|
"main.h": "c",
|
||||||
|
"uart.h": "c",
|
||||||
|
"steppers.h": "c",
|
||||||
|
"pinning.h": "c",
|
||||||
|
"ltp1245.h": "c",
|
||||||
|
"stm32f103x6.h": "c",
|
||||||
|
"stm32f1xx.h": "c",
|
||||||
|
"stm32f100xb.h": "c"
|
||||||
|
},
|
||||||
|
"C_Cpp.intelliSenseEngineFallback": "Enabled"
|
||||||
|
}
|
65
.vscode/tasks.json
vendored
Executable file
65
.vscode/tasks.json
vendored
Executable file
|
@ -0,0 +1,65 @@
|
||||||
|
{
|
||||||
|
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||||
|
// for the documentation about the tasks.json format
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"taskName": "make",
|
||||||
|
"identifier": "make",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "make",
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
},
|
||||||
|
"problemMatcher": "$gcc",
|
||||||
|
"isBackground": false,
|
||||||
|
"presentation": {
|
||||||
|
"echo": true,
|
||||||
|
"reveal": "always",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "shared"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"taskName": "openocd",
|
||||||
|
"identifier": "openocd",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "openocd",
|
||||||
|
"args": [
|
||||||
|
"-f",
|
||||||
|
"interface/stlink-v2.cfg",
|
||||||
|
"-f",
|
||||||
|
"target/stm32f1x.cfg"
|
||||||
|
],
|
||||||
|
"group": "none",
|
||||||
|
"isBackground": true,
|
||||||
|
"presentation": {
|
||||||
|
"echo": true,
|
||||||
|
"reveal": "silent",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "dedicated"
|
||||||
|
},
|
||||||
|
"dependsOn": "make",
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"taskName": "program",
|
||||||
|
"identifier": "program",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "make",
|
||||||
|
"args": [
|
||||||
|
"program"
|
||||||
|
],
|
||||||
|
"group": "build",
|
||||||
|
"problemMatcher": "$gcc",
|
||||||
|
"isBackground": false,
|
||||||
|
"presentation": {
|
||||||
|
"echo": true,
|
||||||
|
"reveal": "always",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "shared"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
1
build-number.txt
Normal file
1
build-number.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
124
|
169
ld/STM32F103C8T6_FLASH.ld
Executable file
169
ld/STM32F103C8T6_FLASH.ld
Executable file
|
@ -0,0 +1,169 @@
|
||||||
|
/*
|
||||||
|
*****************************************************************************
|
||||||
|
**
|
||||||
|
|
||||||
|
** File : LinkerScript.ld
|
||||||
|
**
|
||||||
|
** Abstract : Linker script for STM32F103C8T6 Device with
|
||||||
|
** 64 FLASH, 20 RAM
|
||||||
|
**
|
||||||
|
** Set heap size, stack size and stack location according
|
||||||
|
** to application requirements.
|
||||||
|
**
|
||||||
|
** Set memory bank area and size if external memory is used.
|
||||||
|
**
|
||||||
|
** Target : STMicroelectronics STM32
|
||||||
|
**
|
||||||
|
**
|
||||||
|
** Distribution: The file is distributed as is, without any warranty
|
||||||
|
** of any kind.
|
||||||
|
**
|
||||||
|
** (c)Copyright Ac6.
|
||||||
|
** You may use this file as-is or modify it according to the needs of your
|
||||||
|
** project. Distribution of this file (unmodified or modified) is not
|
||||||
|
** permitted. Ac6 permit registered System Workbench for MCU users the
|
||||||
|
** rights to distribute the assembled, compiled & linked contents of this
|
||||||
|
** file as part of an application binary file, provided that it is built
|
||||||
|
** using the System Workbench for MCU toolchain.
|
||||||
|
**
|
||||||
|
*****************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Entry Point */
|
||||||
|
ENTRY(Reset_Handler)
|
||||||
|
|
||||||
|
/* Highest address of the user mode stack */
|
||||||
|
_estack = 0x20004FFF; /* end of RAM */
|
||||||
|
/* Generate a link error if heap and stack don't fit into RAM */
|
||||||
|
_Min_Heap_Size = 0x200; /* required amount of heap */
|
||||||
|
_Min_Stack_Size = 0x400; /* required amount of stack */
|
||||||
|
|
||||||
|
/* Specify the memory areas */
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 64K
|
||||||
|
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Define output sections */
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
/* The startup code goes first into FLASH */
|
||||||
|
.isr_vector :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
KEEP(*(.isr_vector)) /* Startup code */
|
||||||
|
. = ALIGN(4);
|
||||||
|
} >FLASH
|
||||||
|
|
||||||
|
/* The program code and other data goes into FLASH */
|
||||||
|
.text :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
*(.text) /* .text sections (code) */
|
||||||
|
*(.text*) /* .text* sections (code) */
|
||||||
|
*(.glue_7) /* glue arm to thumb code */
|
||||||
|
*(.glue_7t) /* glue thumb to arm code */
|
||||||
|
*(.eh_frame)
|
||||||
|
|
||||||
|
KEEP (*(.init))
|
||||||
|
KEEP (*(.fini))
|
||||||
|
|
||||||
|
. = ALIGN(4);
|
||||||
|
_etext = .; /* define a global symbols at end of code */
|
||||||
|
} >FLASH
|
||||||
|
|
||||||
|
/* Constant data goes into FLASH */
|
||||||
|
.rodata :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
*(.rodata) /* .rodata sections (constants, strings, etc.) */
|
||||||
|
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
|
||||||
|
. = ALIGN(4);
|
||||||
|
} >FLASH
|
||||||
|
|
||||||
|
.ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
|
||||||
|
.ARM : {
|
||||||
|
__exidx_start = .;
|
||||||
|
*(.ARM.exidx*)
|
||||||
|
__exidx_end = .;
|
||||||
|
} >FLASH
|
||||||
|
|
||||||
|
.preinit_array :
|
||||||
|
{
|
||||||
|
PROVIDE_HIDDEN (__preinit_array_start = .);
|
||||||
|
KEEP (*(.preinit_array*))
|
||||||
|
PROVIDE_HIDDEN (__preinit_array_end = .);
|
||||||
|
} >FLASH
|
||||||
|
.init_array :
|
||||||
|
{
|
||||||
|
PROVIDE_HIDDEN (__init_array_start = .);
|
||||||
|
KEEP (*(SORT(.init_array.*)))
|
||||||
|
KEEP (*(.init_array*))
|
||||||
|
PROVIDE_HIDDEN (__init_array_end = .);
|
||||||
|
} >FLASH
|
||||||
|
.fini_array :
|
||||||
|
{
|
||||||
|
PROVIDE_HIDDEN (__fini_array_start = .);
|
||||||
|
KEEP (*(SORT(.fini_array.*)))
|
||||||
|
KEEP (*(.fini_array*))
|
||||||
|
PROVIDE_HIDDEN (__fini_array_end = .);
|
||||||
|
} >FLASH
|
||||||
|
|
||||||
|
/* used by the startup to initialize data */
|
||||||
|
_sidata = LOADADDR(.data);
|
||||||
|
|
||||||
|
/* Initialized data sections goes into RAM, load LMA copy after code */
|
||||||
|
.data :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
_sdata = .; /* create a global symbol at data start */
|
||||||
|
*(.data) /* .data sections */
|
||||||
|
*(.data*) /* .data* sections */
|
||||||
|
|
||||||
|
. = ALIGN(4);
|
||||||
|
_edata = .; /* define a global symbol at data end */
|
||||||
|
} >RAM AT> FLASH
|
||||||
|
|
||||||
|
|
||||||
|
/* Uninitialized data section */
|
||||||
|
. = ALIGN(4);
|
||||||
|
.bss :
|
||||||
|
{
|
||||||
|
/* This is used by the startup in order to initialize the .bss secion */
|
||||||
|
_sbss = .; /* define a global symbol at bss start */
|
||||||
|
__bss_start__ = _sbss;
|
||||||
|
*(.bss)
|
||||||
|
*(.bss*)
|
||||||
|
*(COMMON)
|
||||||
|
|
||||||
|
. = ALIGN(4);
|
||||||
|
_ebss = .; /* define a global symbol at bss end */
|
||||||
|
__bss_end__ = _ebss;
|
||||||
|
} >RAM
|
||||||
|
|
||||||
|
/* User_heap_stack section, used to check that there is enough RAM left */
|
||||||
|
._user_heap_stack :
|
||||||
|
{
|
||||||
|
. = ALIGN(8);
|
||||||
|
PROVIDE ( end = . );
|
||||||
|
PROVIDE ( _end = . );
|
||||||
|
. = . + _Min_Heap_Size;
|
||||||
|
. = . + _Min_Stack_Size;
|
||||||
|
. = ALIGN(8);
|
||||||
|
} >RAM
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Remove information from the standard libraries */
|
||||||
|
/DISCARD/ :
|
||||||
|
{
|
||||||
|
libc.a ( * )
|
||||||
|
libm.a ( * )
|
||||||
|
libgcc.a ( * )
|
||||||
|
}
|
||||||
|
|
||||||
|
.ARM.attributes 0 : { *(.ARM.attributes) }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
125
makefile
Executable file
125
makefile
Executable file
|
@ -0,0 +1,125 @@
|
||||||
|
PROJECT = main
|
||||||
|
SOURCE_DIRS = src
|
||||||
|
ADDITIONAL_SOURCES =
|
||||||
|
INCLUDE_DIRS =
|
||||||
|
EXCLUDE_SOURCES =
|
||||||
|
BUILD_DIR = build
|
||||||
|
|
||||||
|
DEBUG := yes
|
||||||
|
|
||||||
|
CUBE_DIR ?= /opt/stm32cube/STM32Cube_FW_F1_V1.6.0
|
||||||
|
CUBE_DEVICE = STM32F1xx
|
||||||
|
H_DEVICE = STM32F103xB
|
||||||
|
STARTUP_SOURCE_DIR = $(CUBE_DIR)/Drivers/CMSIS/Device/ST/$(CUBE_DEVICE)/Source/Templates/gcc
|
||||||
|
STARTUP_SOURCES = $(STARTUP_SOURCE_DIR)/startup_stm32f103xb.s
|
||||||
|
LD_SCRIPT = ld/STM32F103C8T6_FLASH.ld
|
||||||
|
|
||||||
|
ifeq ($(DEBUG),yes)
|
||||||
|
DEBUG_FLAGS = -DDEBUG -g3
|
||||||
|
endif
|
||||||
|
|
||||||
|
CFLAGS = -mcpu=cortex-m3 -mthumb \
|
||||||
|
-Os -fno-common -Werror \
|
||||||
|
-Wall -Xlinker --gc-sections -I$(CUBE_DIR)/Drivers/CMSIS/Include \
|
||||||
|
-I$(CUBE_DIR)/Drivers/CMSIS/Device/ST/$(CUBE_DEVICE)/Include \
|
||||||
|
-D$(H_DEVICE) -D_DEFAULT_SOURCE -T$(LD_SCRIPT) \
|
||||||
|
-Wl,-Map=$(BUILD_DIR)/$(PROJECT).map -std=c99 \
|
||||||
|
$(addprefix -I,$(SOURCE_DIRS) $(INCLUDE_DIRS)) $(DEBUG_FLAGS)
|
||||||
|
|
||||||
|
RM = rm -f
|
||||||
|
CC = arm-none-eabi-gcc
|
||||||
|
OBJCOPY = arm-none-eabi-objcopy
|
||||||
|
SIZE = arm-none-eabi-size
|
||||||
|
OBJDUMP = arm-none-eabi-objdump
|
||||||
|
|
||||||
|
BUILD_NUMBER_FILE = build-number.txt
|
||||||
|
BUILD_ID_FLAGS = -Xlinker --defsym -Xlinker __BUILD_DATE=$$(date +'%Y%m%d') \
|
||||||
|
-Xlinker --defsym -Xlinker __BUILD_NUMBER=$$(cat $(BUILD_NUMBER_FILE))
|
||||||
|
|
||||||
|
CURRENT_BUILD_CONFIG := $(shell cat makefile | md5sum) DEBUG = $(DEBUG) CUBE_DIR = $(CUBE_DIR)
|
||||||
|
LAST_BUILD_CONFIG := $(shell if [ -e $(BUILD_DIR)/build-config.txt ] ; then cat $(BUILD_DIR)/build-config.txt ; fi)
|
||||||
|
|
||||||
|
SOURCES = $(filter-out $(addprefix %/,$(EXCLUDE_SOURCES)),$(foreach dir,$(SOURCE_DIRS),$(wildcard $(dir)/*.c))) \
|
||||||
|
$(ADDITIONAL_SOURCES)
|
||||||
|
OBJECTS = $(addprefix $(BUILD_DIR)/,$(addsuffix .o,$(basename $(notdir $(SOURCES)))))
|
||||||
|
DEPENDS = $(addprefix $(BUILD_DIR)/,$(addsuffix .d,$(basename $(notdir $(SOURCES)))))
|
||||||
|
STARTUP_OBJECTS = $(patsubst $(STARTUP_SOURCE_DIR)/%.s, $(BUILD_DIR)/%.o, $(STARTUP_SOURCES))
|
||||||
|
|
||||||
|
.DEFAULT_GOAL = all
|
||||||
|
.DELETE_ON_ERROR:
|
||||||
|
|
||||||
|
define define_compile_rules
|
||||||
|
$(addprefix $(BUILD_DIR)/,$(addsuffix .o,$(basename $(filter-out $(EXCLUDE_SOURCES),$(notdir $(wildcard $(1)/*.c)))))): $(BUILD_DIR)/%.o: $(1)/%.c
|
||||||
|
@echo " CC $$@"
|
||||||
|
@$$(CC) $$(CFLAGS) -o $$@ -c $$<
|
||||||
|
|
||||||
|
$(addprefix $(BUILD_DIR)/,$(addsuffix .d,$(basename $(filter-out $(EXCLUDE_SOURCES),$(notdir $(wildcard $(1)/*.c)))))): $(BUILD_DIR)/%.d: $(1)/%.c
|
||||||
|
@#echo " DP $$@"
|
||||||
|
@set -e; rm -f $$@; $$(CC) -MM $$(CFLAGS) $$< > $$@.$$$$$$$$; sed 's,\($$*\)\.o[ :]*,build\/\1.o $$@ : ,g' < $$@.$$$$$$$$ > $$@; rm -f $$@.$$$$$$$$
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(foreach directory,$(SOURCE_DIRS),$(eval $(call define_compile_rules,$(directory))))
|
||||||
|
|
||||||
|
# Additional sources
|
||||||
|
define define_compile_rule
|
||||||
|
$(addprefix $(BUILD_DIR)/,$(notdir $(1:.c=.o))): $(1)
|
||||||
|
@echo " CC $$@"
|
||||||
|
@$$(CC) $$(CFLAGS) -o $$@ -c $$<
|
||||||
|
|
||||||
|
$(addprefix $(BUILD_DIR)/,$(notdir $(1:.c=.d))): $(1)
|
||||||
|
@#echo " DP $$@"
|
||||||
|
@set -e; rm -f $$@; $$(CC) -MM $$(CFLAGS) $$< > $$@.$$$$$$$$; sed 's,\($$*\)\.o[ :]*,build\/\1.o $$@ : ,g' < $$@.$$$$$$$$ > $$@; rm -f $$@.$$$$$$$$
|
||||||
|
endef
|
||||||
|
|
||||||
|
$(foreach src,$(ADDITIONAL_SOURCES),$(eval $(call define_compile_rule,$(src))))
|
||||||
|
|
||||||
|
$(STARTUP_OBJECTS): $(BUILD_DIR)/%.o: $(STARTUP_SOURCE_DIR)/%.s
|
||||||
|
@echo " AS $@"
|
||||||
|
@$(CC) $< -c -o $@ $(CFLAGS)
|
||||||
|
|
||||||
|
$(DEPENDS):| $(BUILD_DIR)
|
||||||
|
|
||||||
|
include $(DEPENDS)
|
||||||
|
|
||||||
|
$(BUILD_DIR)/$(PROJECT).elf: $(OBJECTS) $(STARTUP_OBJECTS) $(BUILD_NUMBER_FILE)
|
||||||
|
@echo " LD $@"
|
||||||
|
@$(CC) $(OBJECTS) $(STARTUP_OBJECTS) $(CFLAGS) $(BUILD_ID_FLAGS) -o $@
|
||||||
|
|
||||||
|
$(BUILD_DIR)/$(PROJECT).bin: $(BUILD_DIR)/$(PROJECT).elf
|
||||||
|
@echo " OC $@"
|
||||||
|
@$(OBJCOPY) -O binary -S $< $@
|
||||||
|
|
||||||
|
$(BUILD_DIR)/$(PROJECT).lst: $(BUILD_DIR)/$(PROJECT).elf
|
||||||
|
@echo " OD $@"
|
||||||
|
@$(OBJDUMP) -h -S $< > $@
|
||||||
|
|
||||||
|
$(BUILD_DIR):
|
||||||
|
@if [ ! -d "$(BUILD_DIR)" ]; then mkdir "$(BUILD_DIR)"; fi
|
||||||
|
|
||||||
|
$(BUILD_NUMBER_FILE): $(OBJECTS) $(STARTUP_OBJECTS)
|
||||||
|
@if ! test -f $(BUILD_NUMBER_FILE); then echo 0 > $(BUILD_NUMBER_FILE); else \
|
||||||
|
echo $$(($$(cat $(BUILD_NUMBER_FILE)) + 1)) > $(BUILD_NUMBER_FILE) ; fi
|
||||||
|
|
||||||
|
# Rebuild everything in case of a makefile/configuration change
|
||||||
|
.PHONY: all
|
||||||
|
ifneq ("$(CURRENT_BUILD_CONFIG)","$(LAST_BUILD_CONFIG)")
|
||||||
|
all: clean incrementalbuild
|
||||||
|
else
|
||||||
|
all: incrementalbuild
|
||||||
|
endif
|
||||||
|
|
||||||
|
.PHONY: incrementalbuild
|
||||||
|
incrementalbuild: $(BUILD_DIR) $(OBJECTS) $(STARTUP_OBJECTS) $(BUILD_DIR)/$(PROJECT).elf $(BUILD_DIR)/$(PROJECT).bin $(BUILD_DIR)/$(PROJECT).lst
|
||||||
|
@echo " SZ $(BUILD_DIR)/$(PROJECT).elf"
|
||||||
|
@$(SIZE) $(BUILD_DIR)/$(PROJECT).elf
|
||||||
|
@echo "$(CURRENT_BUILD_CONFIG)" > $(BUILD_DIR)/build-config.txt
|
||||||
|
|
||||||
|
.PHONY: program
|
||||||
|
program: $(BUILD_DIR)/$(PROJECT).bin
|
||||||
|
@#if ps -e | grep openocd ; then arm-none-eabi-gdb -batch -x flash.gdb ; else st-flash --reset write $(BUILD_DIR)/$(PROJECT).bin 0x8000000 ; fi
|
||||||
|
st-flash --reset write $(BUILD_DIR)/$(PROJECT).bin 0x8000000
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean:
|
||||||
|
@echo " RM $(BUILD_DIR)/*"
|
||||||
|
@$(RM) $(BUILD_DIR)/*
|
10
src/buildid.h
Executable file
10
src/buildid.h
Executable file
|
@ -0,0 +1,10 @@
|
||||||
|
#ifndef BUILDID_H_
|
||||||
|
#define BUILDID_H_
|
||||||
|
|
||||||
|
extern char __BUILD_DATE;
|
||||||
|
extern char __BUILD_NUMBER;
|
||||||
|
|
||||||
|
#define BUILD_DATE ((uint32_t)&__BUILD_DATE)
|
||||||
|
#define BUILD_NUMBER ((uint32_t)&__BUILD_NUMBER)
|
||||||
|
|
||||||
|
#endif
|
29
src/debug.c
Executable file
29
src/debug.c
Executable file
|
@ -0,0 +1,29 @@
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
void Debug_Print(const char *message)
|
||||||
|
{
|
||||||
|
uint32_t m[] = {2, (uint32_t)message, strlen(message)};
|
||||||
|
__asm__("mov r0, #0x05;"
|
||||||
|
"mov r1, %[m];"
|
||||||
|
"bkpt #0xAB"
|
||||||
|
:
|
||||||
|
: [m] "r" (m)
|
||||||
|
: "r0", "r1", "memory");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Debug_PutChar(char c)
|
||||||
|
{
|
||||||
|
uint32_t m[] = {2, (uint32_t)(&c), 1};
|
||||||
|
__asm__("mov r0, #0x05;"
|
||||||
|
"mov r1, %[m];"
|
||||||
|
"bkpt #0xAB"
|
||||||
|
:
|
||||||
|
: [m] "r" (m)
|
||||||
|
: "r0", "r1", "memory");
|
||||||
|
// __asm__("mov r0, #0x03;"
|
||||||
|
// "mov r1, %[msg];"
|
||||||
|
// "bkpt #0xAB"
|
||||||
|
// :
|
||||||
|
// : [msg] "r" (&c)
|
||||||
|
// : "r0", "r1");
|
||||||
|
}
|
9
src/debug.h
Executable file
9
src/debug.h
Executable file
|
@ -0,0 +1,9 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "stm32f1xx.h"
|
||||||
|
|
||||||
|
void Debug_Print(const char *message);
|
||||||
|
void Debug_PutChar(char c);
|
423
src/ltp1245.c
Normal file
423
src/ltp1245.c
Normal file
|
@ -0,0 +1,423 @@
|
||||||
|
#include "ltp1245.h"
|
||||||
|
#include "pinning.h"
|
||||||
|
|
||||||
|
#define LINEWIDTH (64 * 6)
|
||||||
|
uint8_t LTP1245_Buffer[LINEWIDTH / 8 * LTP1245_BUFFER_LINES];
|
||||||
|
|
||||||
|
typedef struct State_t
|
||||||
|
{
|
||||||
|
struct State_t (*fn)(void);
|
||||||
|
} State_t;
|
||||||
|
|
||||||
|
static volatile int Stepper_Delta = 0;
|
||||||
|
static int PrintLines;
|
||||||
|
static int CurrentBufferLine;
|
||||||
|
bool Printing = false;
|
||||||
|
static volatile int PulseWidth = 2000;
|
||||||
|
|
||||||
|
static void InitStepper(void)
|
||||||
|
{
|
||||||
|
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
|
||||||
|
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
|
||||||
|
|
||||||
|
GPIOA->CRL = (GPIOA->CRL
|
||||||
|
& ~(0x0f << (4 * PIN_STEPPER_AM))
|
||||||
|
& ~(0x0f << (4 * PIN_STEPPER_AP))
|
||||||
|
& ~(0x0f << (4 * PIN_STEPPER_BM))
|
||||||
|
& ~(0x0f << (4 * PIN_STEPPER_BP)))
|
||||||
|
| (0x01 << (4 * PIN_STEPPER_AM)) // Output, max. 10 MHz
|
||||||
|
| (0x01 << (4 * PIN_STEPPER_AP)) // Output, max. 10 MHz
|
||||||
|
| (0x01 << (4 * PIN_STEPPER_BM)) // Output, max. 10 MHz
|
||||||
|
| (0x01 << (4 * PIN_STEPPER_BP)) // Output, max. 10 MHz
|
||||||
|
;
|
||||||
|
|
||||||
|
TIM3->PSC = 72000000 / 100 / LTP1245_MAX_DRIVE_FREQ - 1;
|
||||||
|
TIM3->ARR = 100;
|
||||||
|
TIM3->DIER = TIM_DIER_UIE;
|
||||||
|
TIM3->CR1 = TIM_CR1_CEN;
|
||||||
|
|
||||||
|
NVIC_EnableIRQ(TIM3_IRQn);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void InitSensors(void)
|
||||||
|
{
|
||||||
|
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
|
||||||
|
|
||||||
|
GPIOA->CRL = (GPIOA->CRL
|
||||||
|
& ~(0x0f << (4 * PIN_HEAD))
|
||||||
|
& ~(0x0f << (4 * PIN_PAPER)))
|
||||||
|
| (0x08 << (4 * PIN_HEAD)) // Input with pull-up/pull-down
|
||||||
|
| (0x08 << (4 * PIN_PAPER)) // Input with pull-up/pull-down
|
||||||
|
;
|
||||||
|
|
||||||
|
// Use pull-ups
|
||||||
|
GPIOA->BSRR = (1 << PIN_HEAD) | (1 << PIN_PAPER);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void InitDataLines(void)
|
||||||
|
{
|
||||||
|
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
|
||||||
|
RCC->APB2ENR |= RCC_APB2ENR_AFIOEN;
|
||||||
|
RCC->APB1ENR |= RCC_APB1ENR_SPI2EN;
|
||||||
|
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
|
||||||
|
|
||||||
|
TIM2->PSC = 719; // Each tick corresponds to ten microseconds
|
||||||
|
TIM2->ARR = 201; // 2 milliseconds
|
||||||
|
TIM2->CCR3 = 1;
|
||||||
|
TIM2->CCR4 = 1;
|
||||||
|
TIM2->CR1 = TIM_CR1_OPM; // One-pulse mode
|
||||||
|
TIM2->CCMR2 = TIM_CCMR2_OC3M // CH3 PWM mode 2
|
||||||
|
| TIM_CCMR2_OC4M // CH4 PWM mode 1
|
||||||
|
;
|
||||||
|
TIM2->CCER = 0;
|
||||||
|
TIM2->DIER = 0;
|
||||||
|
|
||||||
|
// Remap TIM2 lines
|
||||||
|
AFIO->MAPR |= AFIO_MAPR_TIM2_REMAP_FULLREMAP;
|
||||||
|
|
||||||
|
GPIOB->BRR = (1 << PIN_DST1) | (1 << PIN_DST2) | (1 << PIN_LATCH);
|
||||||
|
GPIOB->CRH = (GPIOB->CRH
|
||||||
|
& ~(0x0f << (4 * PIN_DST1 - 32))
|
||||||
|
& ~(0x0f << (4 * PIN_DST2 - 32))
|
||||||
|
& ~(0x0f << (4 * PIN_LATCH - 32))
|
||||||
|
& ~(0x0f << (4 * PIN_SCK - 32))
|
||||||
|
& ~(0x0f << (4 * PIN_DIN - 32)))
|
||||||
|
| (0x09 << (4 * PIN_DST1 - 32)) // Output in AF mode, max. 10 MHz
|
||||||
|
| (0x09 << (4 * PIN_DST2 - 32)) // Output in AF mode, max. 10 MHz
|
||||||
|
| (0x01 << (4 * PIN_LATCH - 32)) // Output, max. 10 MHz
|
||||||
|
| (0x09 << (4 * PIN_SCK - 32)) // Output in AF mode, max. 10 MHz
|
||||||
|
| (0x09 << (4 * PIN_DIN - 32)) // Output in AF mode, max. 10 MHz
|
||||||
|
;
|
||||||
|
|
||||||
|
SPI2->CR1 = SPI_CR1_BIDIMODE | SPI_CR1_BIDIOE | SPI_CR1_SPE | SPI_CR1_MSTR
|
||||||
|
| SPI_CR1_BR_2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void InitThermistor(void)
|
||||||
|
{
|
||||||
|
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
|
||||||
|
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
|
||||||
|
|
||||||
|
GPIOB->CRL = (GPIOB->CRL
|
||||||
|
& ~(0x0f << PIN_THERMISTOR))
|
||||||
|
| (0x00 << PIN_THERMISTOR) // Analog mode
|
||||||
|
;
|
||||||
|
|
||||||
|
// Enable ADC
|
||||||
|
ADC1->CR2 |= ADC_CR2_ADON;
|
||||||
|
for(volatile int i = 0; i < 100; i++);
|
||||||
|
|
||||||
|
// Calibrate ADC
|
||||||
|
ADC1->CR2 |= ADC_CR2_CAL;
|
||||||
|
while(ADC1->CR2 & ADC_CR2_CAL);
|
||||||
|
|
||||||
|
// Enable EOC interrupt
|
||||||
|
ADC1->CR1 = ADC_CR1_EOCIE;
|
||||||
|
NVIC_EnableIRQ(ADC1_2_IRQn);
|
||||||
|
|
||||||
|
// The thermistor is connected to ADC12_IN8 (PB0)
|
||||||
|
ADC1->SQR3 = (8 << ADC_SQR3_SQ1_Pos);
|
||||||
|
ADC1->SMPR2 = (7 << ADC_SMPR2_SMP8_Pos);
|
||||||
|
|
||||||
|
// Start conversion, continuous mode
|
||||||
|
ADC1->CR2 |= ADC_CR2_CONT;
|
||||||
|
ADC1->CR2 |= ADC_CR2_ADON;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HasPaper(void)
|
||||||
|
{
|
||||||
|
return !(GPIOA->IDR & (1 << PIN_PAPER));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool HeadDown(void)
|
||||||
|
{
|
||||||
|
return !(GPIOA->IDR & (1 << PIN_HEAD));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActivateHead(int mask)
|
||||||
|
{
|
||||||
|
// Wait until timer has finished
|
||||||
|
while(TIM2->CR1 & TIM_CR1_CEN);
|
||||||
|
|
||||||
|
// Set activation pulse width
|
||||||
|
TIM2->ARR = (PulseWidth / 5) + 1;
|
||||||
|
|
||||||
|
TIM2->CCER = 0;
|
||||||
|
if(mask & 1)
|
||||||
|
{
|
||||||
|
TIM2->CCER |= TIM_CCER_CC4E;
|
||||||
|
}
|
||||||
|
if(mask & 2)
|
||||||
|
{
|
||||||
|
TIM2->CCER |= TIM_CCER_CC3E;
|
||||||
|
}
|
||||||
|
TIM2->CNT = 0;
|
||||||
|
TIM2->EGR = TIM_EGR_UG;
|
||||||
|
TIM2->CR1 |= TIM_CR1_CEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SendLine(uint8_t *line)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < LINEWIDTH / 8; i++)
|
||||||
|
{
|
||||||
|
while(~SPI2->SR & SPI_SR_TXE);
|
||||||
|
*((volatile uint8_t*)(&SPI2->DR)) = line[i];
|
||||||
|
}
|
||||||
|
while(SPI2->SR & SPI_SR_BSY);
|
||||||
|
GPIOB->BSRR = (1 << PIN_LATCH);
|
||||||
|
for(volatile int i = 0; i < 1000; i++);
|
||||||
|
GPIOB->BRR = (1 << PIN_LATCH);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main state machine states
|
||||||
|
|
||||||
|
static State_t State_Idle(void);
|
||||||
|
static State_t State_PaperLoad(void);
|
||||||
|
static State_t State_InitialPaperFeed(void);
|
||||||
|
static State_t State_Printing(void);
|
||||||
|
static State_t State_FinalPaperFeed(void);
|
||||||
|
|
||||||
|
// Automatic paper loading
|
||||||
|
static State_t State_PaperLoad(void)
|
||||||
|
{
|
||||||
|
if(!HasPaper())
|
||||||
|
{
|
||||||
|
if(HeadDown())
|
||||||
|
{
|
||||||
|
Stepper_Delta = 0;
|
||||||
|
}
|
||||||
|
else if(Stepper_Delta < 1000)
|
||||||
|
{
|
||||||
|
Stepper_Delta = 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(Stepper_Delta == 0)
|
||||||
|
{
|
||||||
|
return (State_t){State_Idle};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (State_t){State_PaperLoad};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paper feed before printing
|
||||||
|
static State_t State_InitialPaperFeed(void)
|
||||||
|
{
|
||||||
|
if(Stepper_Delta == 0)
|
||||||
|
{
|
||||||
|
return (State_t){State_Printing};
|
||||||
|
}
|
||||||
|
return (State_t){State_InitialPaperFeed};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actual printing
|
||||||
|
static State_t State_Printing(void)
|
||||||
|
{
|
||||||
|
if(!HeadDown() || !HasPaper())
|
||||||
|
{
|
||||||
|
Printing = false;
|
||||||
|
return (State_t){State_Idle};
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Stepper_Delta == 1)
|
||||||
|
{
|
||||||
|
ActivateHead(3);
|
||||||
|
}
|
||||||
|
else if(Stepper_Delta == 0)
|
||||||
|
{
|
||||||
|
SendLine(LTP1245_Buffer + CurrentBufferLine * LINEWIDTH / 8);
|
||||||
|
ActivateHead(3);
|
||||||
|
CurrentBufferLine++;
|
||||||
|
if(CurrentBufferLine >= LTP1245_BUFFER_LINES)
|
||||||
|
{
|
||||||
|
CurrentBufferLine = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
PrintLines--;
|
||||||
|
|
||||||
|
if(PrintLines > 0)
|
||||||
|
{
|
||||||
|
Stepper_Delta = 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Stepper_Delta = 500;
|
||||||
|
return (State_t){State_FinalPaperFeed};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (State_t){State_Printing};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Paper feed after printing
|
||||||
|
static State_t State_FinalPaperFeed(void)
|
||||||
|
{
|
||||||
|
if(!HasPaper() || !HeadDown())
|
||||||
|
{
|
||||||
|
Printing = false;
|
||||||
|
return (State_t){State_Idle};
|
||||||
|
}
|
||||||
|
if(Stepper_Delta == 0)
|
||||||
|
{
|
||||||
|
Printing = false;
|
||||||
|
return (State_t){State_Idle};
|
||||||
|
}
|
||||||
|
return (State_t){State_FinalPaperFeed};
|
||||||
|
}
|
||||||
|
|
||||||
|
static State_t State_Idle(void)
|
||||||
|
{
|
||||||
|
if(!HasPaper())
|
||||||
|
{
|
||||||
|
return (State_t){State_PaperLoad};
|
||||||
|
}
|
||||||
|
if(Printing)
|
||||||
|
{
|
||||||
|
Stepper_Delta = 200;
|
||||||
|
return (State_t){State_InitialPaperFeed};
|
||||||
|
}
|
||||||
|
return (State_t){State_Idle};
|
||||||
|
}
|
||||||
|
|
||||||
|
void LTP1245_Print(void)
|
||||||
|
{
|
||||||
|
PrintLines = LTP1245_BUFFER_LINES * 20;
|
||||||
|
CurrentBufferLine = 0;
|
||||||
|
Printing = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LTP1245_Init(void)
|
||||||
|
{
|
||||||
|
InitDataLines();
|
||||||
|
InitSensors();
|
||||||
|
InitStepper();
|
||||||
|
InitThermistor();
|
||||||
|
|
||||||
|
for(int i = 0; i < sizeof(LTP1245_Buffer); i++)
|
||||||
|
{
|
||||||
|
int shift = (i * 8 / (64 * 6)) % 8;
|
||||||
|
LTP1245_Buffer[i] =(0x11 << shift) | (0x11 >> (8 - shift));
|
||||||
|
}
|
||||||
|
|
||||||
|
LTP1245_Print();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdvanceStateMachine(void)
|
||||||
|
{
|
||||||
|
static State_t state = {State_Idle};
|
||||||
|
|
||||||
|
state = state.fn();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TIM3_IRQHandler(void)
|
||||||
|
{
|
||||||
|
if(TIM3->SR & TIM_SR_UIF)
|
||||||
|
{
|
||||||
|
static int substep = 0;
|
||||||
|
static bool off;
|
||||||
|
const int TABLE[] = {0, 1, 3, 2};
|
||||||
|
const int GPIO_MASK = ((1 << PIN_STEPPER_AM) | (1 << PIN_STEPPER_AP)
|
||||||
|
| (1 << PIN_STEPPER_BM) | (1 << PIN_STEPPER_BP));
|
||||||
|
|
||||||
|
if(Stepper_Delta != 0)
|
||||||
|
{
|
||||||
|
off = false;
|
||||||
|
if(Stepper_Delta > 0)
|
||||||
|
{
|
||||||
|
substep++;
|
||||||
|
if(substep > 3)
|
||||||
|
substep = 0;
|
||||||
|
Stepper_Delta--;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
substep--;
|
||||||
|
if(substep < 0)
|
||||||
|
substep = 3;
|
||||||
|
Stepper_Delta++;
|
||||||
|
}
|
||||||
|
|
||||||
|
GPIOA->ODR = (GPIOA->ODR & ~GPIO_MASK)
|
||||||
|
| ((TABLE[substep] & 1) ? (1 << PIN_STEPPER_AP)
|
||||||
|
: (1 << PIN_STEPPER_AM))
|
||||||
|
| ((TABLE[substep] & 2) ? (1 << PIN_STEPPER_BP)
|
||||||
|
: (1 << PIN_STEPPER_BM));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(off)
|
||||||
|
{
|
||||||
|
GPIOA->ODR = (GPIOA->ODR & ~GPIO_MASK);
|
||||||
|
substep = 0;
|
||||||
|
}
|
||||||
|
off = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
AdvanceStateMachine();
|
||||||
|
|
||||||
|
TIM3->SR &= ~TIM_SR_UIF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ADC1_2_IRQHandler(void)
|
||||||
|
{
|
||||||
|
const int READINGS[] =
|
||||||
|
{
|
||||||
|
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 375.54)), // -40 °C
|
||||||
|
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 275.39)), // -35 °C
|
||||||
|
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 204.55)), // -30 °C
|
||||||
|
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 153.76)), // -25 °C
|
||||||
|
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 116.89)), // -20 °C
|
||||||
|
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 89.82)), // -15 °C
|
||||||
|
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 69.71)), // -10 °C
|
||||||
|
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 54.61)), // -5 °C
|
||||||
|
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 43.17)), // 0 °C
|
||||||
|
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 34.42)), // 5 °C
|
||||||
|
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 27.66)), // 10 °C
|
||||||
|
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 22.40)), // 15 °C
|
||||||
|
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 18.27)), // 20 °C
|
||||||
|
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 15.00)), // 25 °C
|
||||||
|
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 12.40)), // 30 °C
|
||||||
|
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 10.31)), // 35 °C
|
||||||
|
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 8.63)), // 40 °C
|
||||||
|
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 7.26)), // 45 °C
|
||||||
|
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 6.14)), // 50 °C
|
||||||
|
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 5.22)), // 55 °C
|
||||||
|
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 4.46)), // 60 °C
|
||||||
|
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 3.83)), // 65 °C
|
||||||
|
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 3.30)), // 70 °C
|
||||||
|
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 2.86)), // 75 °C
|
||||||
|
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 2.48)), // 80 °C
|
||||||
|
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 2.17)), // 85 °C
|
||||||
|
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 1.90)), // 90 °C
|
||||||
|
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 1.67)), // 95 °C
|
||||||
|
4095.0 * (1.0 - LTP1245_TH_REXT / (LTP1245_TH_REXT + 1.47)) // 100 °C
|
||||||
|
};
|
||||||
|
|
||||||
|
int adc = ADC1->DR;
|
||||||
|
|
||||||
|
// Find first temperature higher than the measured one
|
||||||
|
int lower_entry = 0;
|
||||||
|
for(int i = 1; i < sizeof(READINGS) / sizeof(READINGS[0]); i++)
|
||||||
|
{
|
||||||
|
if(adc >= READINGS[i])
|
||||||
|
{
|
||||||
|
lower_entry = i - 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int higher_entry = lower_entry + 1;
|
||||||
|
int temp = lower_entry * 5 - 40; // Temperature in °C
|
||||||
|
|
||||||
|
// Interpolate linearly
|
||||||
|
if(higher_entry < sizeof(READINGS) / sizeof(READINGS[0]))
|
||||||
|
{
|
||||||
|
int diff = READINGS[lower_entry] - READINGS[higher_entry];
|
||||||
|
temp += (READINGS[lower_entry] - adc) * 5 / diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the formula from section 3.6, adjusted for integer arithmetic and
|
||||||
|
// a pulse with in microseconds
|
||||||
|
PulseWidth = (285 * 178 - (int)(1000 * 178 * 0.003135) * (temp - 25))
|
||||||
|
/ (int)((5 * 1.4 - 2.9) * (5 * 1.4 - 2.9));
|
||||||
|
}
|
14
src/ltp1245.h
Normal file
14
src/ltp1245.h
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include "stm32f1xx.h"
|
||||||
|
|
||||||
|
typedef int (*LTP1245_DataProvider_t)(uint8_t *buffer);
|
||||||
|
|
||||||
|
#define LTP1245_MAX_DRIVE_FREQ 473 // In Hz
|
||||||
|
#define LTP1245_BUFFER_LINES 64
|
||||||
|
|
||||||
|
#define LTP1245_TH_REXT 10 // In kΩ
|
||||||
|
|
||||||
|
void LTP1245_Init(void);
|
12
src/main.c
Executable file
12
src/main.c
Executable file
|
@ -0,0 +1,12 @@
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
USB_Init();
|
||||||
|
LTP1245_Init();
|
||||||
|
|
||||||
|
for(;;)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
15
src/main.h
Executable file
15
src/main.h
Executable file
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef MAIN_H_
|
||||||
|
#define MAIN_H_
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "stm32f1xx.h"
|
||||||
|
|
||||||
|
#include "pinning.h"
|
||||||
|
#include "buildid.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "usb.h"
|
||||||
|
#include "ltp1245.h"
|
||||||
|
|
||||||
|
int main(void);
|
||||||
|
|
||||||
|
#endif
|
23
src/pinning.h
Executable file
23
src/pinning.h
Executable file
|
@ -0,0 +1,23 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Port A
|
||||||
|
#define PIN_STEPPER_AP 0 // PA0 - Stepper phase A positive
|
||||||
|
#define PIN_STEPPER_BP 1 // PA1 - Stepper phase B positive
|
||||||
|
#define PIN_HEAD 2 // PA2 - Head up sensor
|
||||||
|
#define PIN_PAPER 3 // PA3 - Paper detect
|
||||||
|
#define PIN_STEPPER_AM 4 // PA4 - Stepper phase A negative
|
||||||
|
#define PIN_STEPPER_BM 5 // PA5 - Stepper phase B negative
|
||||||
|
|
||||||
|
#define PIN_USB_DM 11 // PA11 - USB D-
|
||||||
|
#define PIN_USB_DP 12 // PA12 - USB D+
|
||||||
|
|
||||||
|
// Port B
|
||||||
|
#define PIN_THERMISTOR 0 // PB0 - Thermistor
|
||||||
|
#define PIN_DST1 10 // PB10 - Head drive signal 1 (TIM2_CH3)
|
||||||
|
#define PIN_DST2 11 // PB11 - Head drive signal 2 (TIM2_CH4)
|
||||||
|
#define PIN_SCK 13 // PB13 - Thermal printer SCK (SPI2)
|
||||||
|
#define PIN_LATCH 14 // PB14 - Thermal printer data latch
|
||||||
|
#define PIN_DIN 15 // PB15 - Thermal printer MOSI (SPI2)
|
||||||
|
|
||||||
|
// Port C
|
||||||
|
#define PIN_LED 13 // PC13 - Status LED
|
32
src/system.c
Executable file
32
src/system.c
Executable file
|
@ -0,0 +1,32 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "stm32f103x6.h"
|
||||||
|
|
||||||
|
void SystemInit(void)
|
||||||
|
{
|
||||||
|
// Activate HSE and wait for it to be ready
|
||||||
|
RCC->CR = RCC_CR_HSEON;
|
||||||
|
while(!(RCC->CR & RCC_CR_HSERDY));
|
||||||
|
|
||||||
|
RCC->CFGR = RCC_CFGR_SW_0;
|
||||||
|
while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_0);
|
||||||
|
|
||||||
|
FLASH->ACR |= FLASH_ACR_LATENCY_1;
|
||||||
|
|
||||||
|
// Set PLL to x9 (-> 72MHz system clock)
|
||||||
|
RCC->CFGR |= RCC_CFGR_PLLMULL9 | RCC_CFGR_PLLSRC | RCC_CFGR_PPRE1_2;
|
||||||
|
|
||||||
|
// Activate PLL and wait
|
||||||
|
RCC->CR |= RCC_CR_PLLON;
|
||||||
|
while(!(RCC->CR & RCC_CR_PLLRDY));
|
||||||
|
|
||||||
|
// Select PLL as clock source
|
||||||
|
RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_SW) | RCC_CFGR_SW_1;
|
||||||
|
|
||||||
|
// Resulting clocks:
|
||||||
|
// SYSCLK, AHB, APB2 72 Mhz
|
||||||
|
// APB1, ADC 36 MHz
|
||||||
|
|
||||||
|
// Disable all interrupts
|
||||||
|
RCC->CIR = 0x00000000;
|
||||||
|
}
|
||||||
|
|
376
src/usb.c
Executable file
376
src/usb.c
Executable file
|
@ -0,0 +1,376 @@
|
||||||
|
#include "usb.h"
|
||||||
|
#include "usb_descriptors.h"
|
||||||
|
#include "usb_com.h"
|
||||||
|
|
||||||
|
uint8_t USB_DeviceStatus[2] = {0x00, 0x01};
|
||||||
|
volatile unsigned int USB_ResetCount = 0;
|
||||||
|
volatile unsigned int USB_Address = 0;
|
||||||
|
|
||||||
|
static inline void USB_SetEPR(volatile uint16_t *EPR, uint16_t status)
|
||||||
|
{
|
||||||
|
// Caution: This function does a read-modify-write and is prone to
|
||||||
|
// unexpected behaviour when there are transactions going one, because the
|
||||||
|
// register contents might change during the funciton's execution. Thus, only
|
||||||
|
// use this function in initialisation code!
|
||||||
|
volatile uint16_t v = *EPR;
|
||||||
|
status ^= v & (USB_EP0R_DTOG_RX | USB_EP0R_STAT_RX |\
|
||||||
|
USB_EP0R_DTOG_TX | USB_EP0R_STAT_TX);
|
||||||
|
*EPR = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void USB_SetEPRXStatus(volatile uint16_t *EPR, uint16_t status)
|
||||||
|
{
|
||||||
|
uint16_t v = *EPR;
|
||||||
|
v ^= status & USB_EP0R_STAT_RX;
|
||||||
|
v &= USB_EP0R_CTR_RX | USB_EP0R_EP_TYPE | USB_EP0R_EP_KIND | USB_EP0R_CTR_TX | USB_EP0R_EA | USB_EP0R_STAT_RX;
|
||||||
|
*EPR = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void USB_SetEPTXStatus(volatile uint16_t *EPR, uint16_t status)
|
||||||
|
{
|
||||||
|
uint16_t v = *EPR;
|
||||||
|
v ^= status & USB_EP0R_STAT_TX;
|
||||||
|
v &= USB_EP0R_CTR_RX | USB_EP0R_EP_TYPE | USB_EP0R_EP_KIND | USB_EP0R_CTR_TX | USB_EP0R_EA | USB_EP0R_STAT_TX;
|
||||||
|
*EPR = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void USB_SetEPType(volatile uint16_t *EPR, uint16_t type)
|
||||||
|
{
|
||||||
|
uint16_t v = *EPR;
|
||||||
|
v &= USB_EP0R_CTR_RX | USB_EP0R_EP_KIND | USB_EP0R_CTR_TX | USB_EP0R_EA;
|
||||||
|
v |= USB_EP0R_EP_TYPE & type;
|
||||||
|
*EPR = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void USB_SetEPAddress(volatile uint16_t *EPR, uint16_t address)
|
||||||
|
{
|
||||||
|
uint16_t v = *EPR;
|
||||||
|
v &= USB_EP0R_CTR_RX | USB_EP0R_EP_TYPE | USB_EP0R_EP_KIND | USB_EP0R_CTR_TX;
|
||||||
|
v |= USB_EP0R_EA & address;
|
||||||
|
*EPR = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void USB_SetEPKind(volatile uint16_t *EPR)
|
||||||
|
{
|
||||||
|
uint16_t v = *EPR;
|
||||||
|
v &= USB_EP0R_CTR_RX | USB_EP0R_EP_TYPE | USB_EP0R_CTR_TX | USB_EP0R_EA;
|
||||||
|
v |= USB_EP0R_EP_KIND;
|
||||||
|
*EPR = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void USB_ClearEPKind(volatile uint16_t *EPR)
|
||||||
|
{
|
||||||
|
uint16_t v = *EPR;
|
||||||
|
v &= USB_EP0R_CTR_RX | USB_EP0R_EP_TYPE | USB_EP0R_CTR_TX | USB_EP0R_EA;
|
||||||
|
*EPR = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void USB_ClearEPCTRX(volatile uint16_t *EPR)
|
||||||
|
{
|
||||||
|
uint16_t v = *EPR;
|
||||||
|
v &= USB_EP0R_EP_TYPE | USB_EP0R_EP_KIND | USB_EP0R_CTR_TX | USB_EP0R_EA;
|
||||||
|
*EPR = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void USB_ClearEPCTTX(volatile uint16_t *EPR)
|
||||||
|
{
|
||||||
|
uint16_t v = *EPR;
|
||||||
|
v &= USB_EP0R_CTR_RX | USB_EP0R_EP_TYPE | USB_EP0R_EP_KIND | USB_EP0R_EA;
|
||||||
|
*EPR = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void USB_Delay(unsigned int delay)
|
||||||
|
{
|
||||||
|
SysTick->LOAD = delay;
|
||||||
|
SysTick->VAL = 0;
|
||||||
|
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;
|
||||||
|
while(!((SysTick->CTRL) & SysTick_CTRL_COUNTFLAG_Msk));
|
||||||
|
SysTick->CTRL = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void USB_Init(void)
|
||||||
|
{
|
||||||
|
// GPIOA clock
|
||||||
|
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
|
||||||
|
RCC->APB1ENR |= RCC_APB1ENR_USBEN;
|
||||||
|
|
||||||
|
// for(int i = 0; i < 8; i++)
|
||||||
|
// {
|
||||||
|
// USB_BTABLE_ENTRIES[i].ADDR_RX = 0;
|
||||||
|
// USB_BTABLE_ENTRIES[i].ADDR_TX = 0;
|
||||||
|
// USB_BTABLE_ENTRIES[i].COUNT_RX = 0;
|
||||||
|
// USB_BTABLE_ENTRIES[i].COUNT_TX = 0;
|
||||||
|
// }
|
||||||
|
|
||||||
|
GPIOA->CRH &= ~(GPIO_CRH_CNF11 | GPIO_CRH_MODE11 |\
|
||||||
|
GPIO_CRH_CNF12 | GPIO_CRH_MODE12);
|
||||||
|
GPIOA->CRH |= GPIO_CRH_MODE11 | GPIO_CRH_MODE12;
|
||||||
|
GPIOA->ODR &= ~(GPIO_CRH_MODE11 | GPIO_CRH_MODE12);
|
||||||
|
USB_Delay(100000);
|
||||||
|
|
||||||
|
// Enable reset and correct transfer interrupts
|
||||||
|
NVIC_EnableIRQ(USB_LP_IRQn);
|
||||||
|
|
||||||
|
// Analog power up
|
||||||
|
USB->CNTR = (uint16_t)USB_CNTR_FRES;
|
||||||
|
// Minimum delay: 1 µs
|
||||||
|
USB_Delay(3000);
|
||||||
|
|
||||||
|
USB->CNTR = (uint16_t)0;
|
||||||
|
USB->ISTR = (uint16_t)0;
|
||||||
|
USB->CNTR = (uint16_t)(USB_CNTR_RESETM | USB_CNTR_CTRM);
|
||||||
|
|
||||||
|
// Configure USB pins (PA11 and PA12 in AF mode, 50 MHz push-pull)
|
||||||
|
GPIOA->CRH |= GPIO_CRH_CNF11_1 | GPIO_CRH_MODE11 |\
|
||||||
|
GPIO_CRH_CNF12_1 | GPIO_CRH_MODE12;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void USB_HandleReset(void)
|
||||||
|
{
|
||||||
|
// Remove reset flag
|
||||||
|
USB->ISTR = (uint16_t)~(USB_ISTR_RESET);
|
||||||
|
USB_ResetCount++;
|
||||||
|
|
||||||
|
// Set buffer table origin
|
||||||
|
USB->BTABLE = USB_BTABLE_OFFSET;
|
||||||
|
|
||||||
|
// Set buffer table contents for control endpoint 0
|
||||||
|
USB_BTABLE_ENTRIES[0].COUNT_RX = USB_EP_RXCOUNT_BL_SIZE | (1 << 10);
|
||||||
|
// USB_BTABLE_ENTRIES[0].COUNT_RX = (4 << 10);
|
||||||
|
USB_BTABLE_ENTRIES[0].ADDR_RX = 0x40;
|
||||||
|
USB_BTABLE_ENTRIES[0].COUNT_TX = 0;
|
||||||
|
USB_BTABLE_ENTRIES[0].ADDR_TX = 0x80;
|
||||||
|
|
||||||
|
// Set buffer table contents for control endpoint 1
|
||||||
|
USB_BTABLE_ENTRIES[1].COUNT_RX_0 = USB_EP_RXCOUNT_BL_SIZE | (1 << 10);
|
||||||
|
USB_BTABLE_ENTRIES[1].ADDR_RX_0 = 0x100;
|
||||||
|
USB_BTABLE_ENTRIES[1].COUNT_RX_1 = USB_EP_RXCOUNT_BL_SIZE | (1 << 10);
|
||||||
|
USB_BTABLE_ENTRIES[1].ADDR_RX_1 = 0x140;
|
||||||
|
|
||||||
|
// Configure endpoint 0
|
||||||
|
USB_SetEPR(&(USB->EP0R), USB_EPR_EP_TYPE_CONTROL |\
|
||||||
|
USB_EPR_STAT_TX_NAK | USB_EPR_STAT_RX_VALID);
|
||||||
|
|
||||||
|
|
||||||
|
// Configure endpoint 1
|
||||||
|
USB_SetEPR(&(USB->EP1R), USB_EPR_EP_TYPE_ISO |\
|
||||||
|
USB_EPR_STAT_TX_DISABLED | USB_EPR_STAT_RX_VALID |\
|
||||||
|
(1 << USB_EP0R_EA_Pos));
|
||||||
|
|
||||||
|
// Enable
|
||||||
|
USB->DADDR = USB_DADDR_EF;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void USB_PMAToMemory(uint8_t *mem, uint16_t offset, size_t length)
|
||||||
|
{
|
||||||
|
uint8_t *pma = (uint8_t*)(USB_PMA_ADDR + 2 * offset);
|
||||||
|
for(unsigned int i = 0; i < length / 2; i++)
|
||||||
|
{
|
||||||
|
mem[2 * i] = *pma++;
|
||||||
|
mem[2 * i + 1] = *pma++;
|
||||||
|
pma += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void USB_MemoryToPMA(uint16_t offset, const uint8_t *mem, size_t length)
|
||||||
|
{
|
||||||
|
uint16_t *pma = (uint16_t*)(USB_PMA_ADDR + 2 * offset);
|
||||||
|
for(unsigned int i = 0; i < length / 2; i++)
|
||||||
|
{
|
||||||
|
uint16_t tmp = mem[2 * i] | (mem[2 * i + 1] << 8);
|
||||||
|
*pma++ = tmp;
|
||||||
|
pma++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void USB_HandleIn(void)
|
||||||
|
{
|
||||||
|
if((USB->DADDR & USB_DADDR_ADD) != USB_Address)
|
||||||
|
{
|
||||||
|
USB->DADDR = USB_Address | USB_DADDR_EF;
|
||||||
|
}
|
||||||
|
// Ready for next packet
|
||||||
|
USB_SetEPRXStatus(&USB->EP0R, USB_EP_RX_VALID);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void USB_HandleOut(void)
|
||||||
|
{
|
||||||
|
// Ready for next packet
|
||||||
|
USB_SetEPRXStatus(&USB->EP0R, USB_EP_RX_VALID);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void USB_HandleSetup(void)
|
||||||
|
{
|
||||||
|
USB_SetupPacket_t sp;
|
||||||
|
//memcpy(&sp, (const void*)(USB_PMA_ADDR + USB_BTABLE_ENTRIES[0].ADDR_RX), sizeof(USB_SetupPacket_t));
|
||||||
|
USB_PMAToMemory((uint8_t*)&sp, USB_BTABLE_ENTRIES[0].ADDR_RX, sizeof(USB_SetupPacket_t));
|
||||||
|
|
||||||
|
const uint8_t *reply_data = NULL;
|
||||||
|
int reply_length = 0;
|
||||||
|
uint8_t reply_response = USB_TOKEN_ACK;
|
||||||
|
|
||||||
|
if((sp.bmRequestType & (USB_REQUEST_TYPE | USB_REQUEST_RECIPIENT)) \
|
||||||
|
== (USB_REQUEST_TYPE_STANDARD | USB_REQUEST_RECIPIENT_DEVICE))
|
||||||
|
{
|
||||||
|
switch(sp.bRequest)
|
||||||
|
{
|
||||||
|
case USB_REQUEST_GET_STATUS:;
|
||||||
|
if(sp.wValue == 0 && sp.wIndex == 0 && sp.wLength == 2)
|
||||||
|
{
|
||||||
|
reply_length = 2;
|
||||||
|
reply_data = USB_DeviceStatus;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case USB_REQUEST_GET_DESCRIPTOR:;
|
||||||
|
USB_DescriptorType_t descriptor_type = sp.wValue >> 8;
|
||||||
|
int descriptor_index = sp.wValue & 0xff;
|
||||||
|
switch(descriptor_type)
|
||||||
|
{
|
||||||
|
case USB_DEVICE_DESCRIPTOR:;
|
||||||
|
reply_data = USB_DeviceDescriptor.raw;
|
||||||
|
reply_length = USB_DeviceDescriptor.bLength;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case USB_CONFIGURATION_DESCRIPTOR:;
|
||||||
|
reply_data = USB_ConfigurationInterfaceDescriptor.raw;
|
||||||
|
if(sp.wLength < USB_ConfigurationInterfaceDescriptor.configuration.wTotalLength)
|
||||||
|
{
|
||||||
|
reply_length = USB_ConfigurationInterfaceDescriptor.configuration.bLength;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reply_length = USB_ConfigurationInterfaceDescriptor.configuration.wTotalLength;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case USB_STRING_DESCRIPTOR:;
|
||||||
|
switch(descriptor_index)
|
||||||
|
{
|
||||||
|
case 0:;
|
||||||
|
reply_data = (uint8_t*)USB_StringDescriptor_LangID;
|
||||||
|
reply_length = (uint8_t)*USB_StringDescriptor_LangID;
|
||||||
|
break;
|
||||||
|
case 1:;
|
||||||
|
reply_data = (uint8_t*)USB_StringDescriptor_Vendor;
|
||||||
|
reply_length = (uint8_t)*USB_StringDescriptor_Vendor;
|
||||||
|
break;
|
||||||
|
case 2:;
|
||||||
|
reply_data = (uint8_t*)USB_StringDescriptor_Product;
|
||||||
|
reply_length = (uint8_t)*USB_StringDescriptor_Product;
|
||||||
|
break;
|
||||||
|
case 3:;
|
||||||
|
reply_data = (uint8_t*)USB_StringDescriptor_Serial;
|
||||||
|
reply_length = (uint8_t)*USB_StringDescriptor_Serial;
|
||||||
|
break;
|
||||||
|
default:;
|
||||||
|
__asm__ volatile("bkpt");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case USB_INTERFACE_DESCRIPTOR:;
|
||||||
|
reply_data = USB_ConfigurationInterfaceDescriptor.interface0.raw;
|
||||||
|
reply_length = USB_ConfigurationInterfaceDescriptor.interface0.bLength;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case USB_ENDPOINT_DESCRIPTOR:;
|
||||||
|
// Does not exist yet
|
||||||
|
__asm__ volatile("bkpt");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case USB_REQUEST_SET_ADDRESS:
|
||||||
|
USB_Address = sp.wValue & USB_DADDR_ADD;
|
||||||
|
reply_response = USB_EP_TX_VALID;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case USB_REQUEST_SET_CONFIGURATION:
|
||||||
|
reply_response = USB_EP_TX_VALID;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:;
|
||||||
|
reply_response = USB_EP_TX_STALL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reply_length = USBCOM_HandleSetupPacket(&sp, &reply_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(reply_data)
|
||||||
|
{
|
||||||
|
// Reply with data
|
||||||
|
//memcpy((void*)(USB_PMA_ADDR + USB_BTABLE_ENTRIES[0].ADDR_TX), reply_data, reply_length);
|
||||||
|
USB_MemoryToPMA(USB_BTABLE_ENTRIES[0].ADDR_TX, reply_data, reply_length);
|
||||||
|
USB_BTABLE_ENTRIES[0].COUNT_TX = reply_length;
|
||||||
|
USB_SetEPTXStatus(&(USB->EP0R), USB_EP_TX_VALID);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Send response
|
||||||
|
USB_BTABLE_ENTRIES[0].COUNT_TX = 0;
|
||||||
|
USB_SetEPTXStatus(&(USB->EP0R), reply_response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interrupt service routine
|
||||||
|
void USB_LP_IRQHandler(void)
|
||||||
|
{
|
||||||
|
if(USB->ISTR & USB_ISTR_RESET)
|
||||||
|
{
|
||||||
|
// Reset happened
|
||||||
|
USB_HandleReset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint16_t istr;
|
||||||
|
while((istr = USB->ISTR) & (USB_ISTR_CTR))
|
||||||
|
{
|
||||||
|
if(istr & USB_ISTR_CTR)
|
||||||
|
{
|
||||||
|
// USB->ISTR = (uint16_t)~USB_ISTR_CTR;
|
||||||
|
// Correct transfer
|
||||||
|
int ep = istr & USB_ISTR_EP_ID;
|
||||||
|
if(ep == 0)
|
||||||
|
{
|
||||||
|
if(istr & USB_ISTR_DIR)
|
||||||
|
{
|
||||||
|
// OUT transfer
|
||||||
|
if(USB->EP0R & USB_EP0R_SETUP)
|
||||||
|
{
|
||||||
|
// Clear CTR_RX
|
||||||
|
USB->EP0R = USB->EP0R & (USB_EP0R_SETUP | USB_EP0R_EP_TYPE |\
|
||||||
|
USB_EP0R_EP_KIND | USB_EP0R_CTR_TX | USB_EP0R_EA);
|
||||||
|
|
||||||
|
// Setup packed received
|
||||||
|
USB_HandleSetup();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Clear CTR_RX
|
||||||
|
USB->EP0R = USB->EP0R & (USB_EP0R_SETUP | USB_EP0R_EP_TYPE |\
|
||||||
|
USB_EP0R_EP_KIND | USB_EP0R_CTR_TX | USB_EP0R_EA);
|
||||||
|
|
||||||
|
USB_HandleOut();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Clear CTR_TX
|
||||||
|
USB->EP0R = USB->EP0R & (USB_EP0R_CTR_RX | USB_EP0R_SETUP |\
|
||||||
|
USB_EP0R_EP_TYPE | USB_EP0R_EP_KIND | USB_EP0R_EA);
|
||||||
|
|
||||||
|
// IN transfer
|
||||||
|
USB_HandleIn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(ep == 1)
|
||||||
|
{
|
||||||
|
USBCOM_HandleISO0OUT();
|
||||||
|
// Ready for next packet
|
||||||
|
// USB_SetEPRXStatus(&USB->EP1R, USB_EP_RX_VALID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
113
src/usb.h
Executable file
113
src/usb.h
Executable file
|
@ -0,0 +1,113 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include "stm32f1xx.h"
|
||||||
|
|
||||||
|
#include "pinning.h"
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
#define USB_TOKEN_OUT 0b0001
|
||||||
|
#define USB_TOKEN_IN 0x1001
|
||||||
|
#define USB_TOKEN_SOF 0b0101
|
||||||
|
#define USB_TOKEN_SETUP 0b1101
|
||||||
|
#define USB_TOKEN_DATA0 0b0011
|
||||||
|
#define USB_TOKEN_DATA1 0b1011
|
||||||
|
#define USB_TOKEN_DATA2 0b0111
|
||||||
|
#define USB_TOKEN_ACK 0b0010
|
||||||
|
#define USB_TOKEN_NAK 0b1010
|
||||||
|
#define USB_TOKEN_STALL 0b1110
|
||||||
|
#define USB_TOKEN_NYET 0b0110
|
||||||
|
#define USB_TOKEN_PRE 0b1100
|
||||||
|
#define USB_TOKEN_ERR 0b1100
|
||||||
|
#define USB_TOKEN_SPLIT 0b1000
|
||||||
|
#define USB_TOKEN_PING 0b0100
|
||||||
|
|
||||||
|
#define USB_REQUEST_DIRECTION (1 << 7)
|
||||||
|
#define USB_REQUEST_DIRECTION_OUT 0
|
||||||
|
#define USB_REQUEST_DIRECTION_IN (1 << 7)
|
||||||
|
#define USB_REQUEST_TYPE (0x3 << 5)
|
||||||
|
#define USB_REQUEST_TYPE_STANDARD 0
|
||||||
|
#define USB_REQUEST_TYPE_CLASS (1 << 5)
|
||||||
|
#define UBS_REQUEST_TYPE_VENDOR (2 << 5)
|
||||||
|
#define UBS_REQUEST_TYPE_RESERVED (3 << 5)
|
||||||
|
#define USB_REQUEST_RECIPIENT 0x1f
|
||||||
|
#define USB_REQUEST_RECIPIENT_DEVICE 0
|
||||||
|
#define USB_REQUEST_RECIPIENT_INTERFACE 1
|
||||||
|
#define USB_REQUEST_RECIPIENT_ENDPOINT 2
|
||||||
|
#define USB_REQUEST_RECIPIENT_OTHER 3
|
||||||
|
|
||||||
|
#define USB_REQUEST_CLEAR_FEATURE 1 // wValue = <feature>, wIndex = 0|<interface>|<endpoint>, wLength = 0
|
||||||
|
#define USB_REQUEST_GET_CONFIGURATION 8 // wValue = 0, wIndex = 0, wLength = 1
|
||||||
|
#define USB_REQUEST_GET_DESCRIPTOR 6 // wValue = <descriptor type>:<descriptor index>, wIndex = 0|<language>, wLength = <descriptor length>
|
||||||
|
#define USB_REQUEST_GET_INTERFACE 10 // wValue = 0, wIndex = <interface>, wLength = 1
|
||||||
|
#define USB_REQUEST_GET_STATUS 0 // wValue = 0, wIndex = 0|<interface>|<endpoint>, wLength = 2
|
||||||
|
#define USB_REQUEST_SET_ADDRESS 5 // wValue = <address>, wIndex = 0, wLength = 0
|
||||||
|
#define USB_REQUEST_SET_CONFIGURATION 9 // wValue = <configuration value>, wIndex = 0, wLength = 0
|
||||||
|
#define USB_REQUEST_SET_DESCRIPTOR 7 // wValue = <descriptor type>:<descriptor index>, wIndex = 0|<language>, wLength = <descriptor length>
|
||||||
|
#define USB_REQUEST_SET_FEATURE 3 // wValue = <feature selector>, wIndex = 0|<interface>|<endpoint>, wLength = 0
|
||||||
|
#define USB_REQUEST_SET_INTERFACE 11 // wValue = <alternate setting>, wIndex = <interface>, wLength = 0
|
||||||
|
#define USB_REQUEST_SYNCH_FRAME 12 // wValue = 0, wIndex = <endpoint>, wLength = 2
|
||||||
|
|
||||||
|
#define USB_EPR_STAT_TX_DISABLED 0x00
|
||||||
|
#define USB_EPR_STAT_TX_STALL USB_EP0R_STAT_TX_0
|
||||||
|
#define USB_EPR_STAT_TX_NAK USB_EP0R_STAT_TX_1
|
||||||
|
#define USB_EPR_STAT_TX_VALID (USB_EP0R_STAT_TX_0 | USB_EP0R_STAT_TX_1)
|
||||||
|
|
||||||
|
#define USB_EPR_STAT_RX_DISABLED 0x00
|
||||||
|
#define USB_EPR_STAT_RX_STALL USB_EP0R_STAT_RX_0
|
||||||
|
#define USB_EPR_STAT_RX_NAK USB_EP0R_STAT_RX_1
|
||||||
|
#define USB_EPR_STAT_RX_VALID (USB_EP0R_STAT_RX_0 | USB_EP0R_STAT_RX_1)
|
||||||
|
|
||||||
|
#define USB_EPR_EP_TYPE_BULK 0x00
|
||||||
|
#define USB_EPR_EP_TYPE_CONTROL USB_EP0R_EP_TYPE_0
|
||||||
|
#define USB_EPR_EP_TYPE_ISO USB_EP0R_EP_TYPE_1
|
||||||
|
#define USB_EPR_EP_TYPE_INTERRUPT (USB_EP0R_EP_TYPE_0 | USB_EP0R_EP_TYPE_1)
|
||||||
|
|
||||||
|
#define USB_PMA_ADDR 0x40006000UL
|
||||||
|
#define USB_BTABLE_OFFSET 0x00
|
||||||
|
#define USB_EP_RXCOUNT_BL_SIZE (1 << 15)
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
volatile union
|
||||||
|
{
|
||||||
|
volatile uint16_t ADDR_TX;
|
||||||
|
volatile uint16_t ADDR_RX_0;
|
||||||
|
volatile uint16_t ADDR_TX_0;
|
||||||
|
};
|
||||||
|
volatile uint16_t rsvd1;
|
||||||
|
volatile union
|
||||||
|
{
|
||||||
|
volatile uint16_t COUNT_TX;
|
||||||
|
volatile uint16_t COUNT_RX_0;
|
||||||
|
volatile uint16_t COUNT_TX_0;
|
||||||
|
};
|
||||||
|
volatile uint16_t rsvd2;
|
||||||
|
volatile union
|
||||||
|
{
|
||||||
|
volatile uint16_t ADDR_RX;
|
||||||
|
volatile uint16_t ADDR_RX_1;
|
||||||
|
volatile uint16_t ADDR_TX_1;
|
||||||
|
};
|
||||||
|
volatile uint16_t rsvd3;
|
||||||
|
volatile union
|
||||||
|
{
|
||||||
|
volatile uint16_t COUNT_RX;
|
||||||
|
volatile uint16_t COUNT_RX_1;
|
||||||
|
volatile uint16_t COUNT_TX_1;
|
||||||
|
};
|
||||||
|
volatile uint16_t rsvd4;
|
||||||
|
} __attribute__((packed, aligned(4))) USB_BufferTableEntry_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t bmRequestType;
|
||||||
|
uint8_t bRequest;
|
||||||
|
uint16_t wValue;
|
||||||
|
uint16_t wIndex;
|
||||||
|
uint16_t wLength;
|
||||||
|
} __attribute__((packed, aligned(2))) USB_SetupPacket_t;
|
||||||
|
|
||||||
|
#define USB_BTABLE_ENTRIES ((USB_BufferTableEntry_t*)(USB_PMA_ADDR + USB_BTABLE_OFFSET))
|
||||||
|
|
||||||
|
void USB_Init(void);
|
33
src/usb_com.c
Executable file
33
src/usb_com.c
Executable file
|
@ -0,0 +1,33 @@
|
||||||
|
#include "usb_com.h"
|
||||||
|
|
||||||
|
unsigned USBCOM_Registers[USBCOM_N_REGISTERS];
|
||||||
|
|
||||||
|
uint8_t USBCOM_HandleSetupPacket(USB_SetupPacket_t *sp, const uint8_t **reply_data)
|
||||||
|
{
|
||||||
|
uint8_t reply_length = 0;
|
||||||
|
|
||||||
|
if(sp->bmRequestType & USB_REQUEST_DIRECTION_IN)
|
||||||
|
{
|
||||||
|
// Read register
|
||||||
|
if(sp->bRequest < USBCOM_N_REGISTERS)
|
||||||
|
{
|
||||||
|
*reply_data = (uint8_t*)&USBCOM_Registers[sp->bRequest];
|
||||||
|
reply_length = sp->wLength;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Write register
|
||||||
|
if(sp->bRequest < USBCOM_N_REGISTERS)
|
||||||
|
{
|
||||||
|
USBCOM_Registers[sp->bRequest] = ((uint32_t)(sp->wIndex) << 16) | sp->wValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return reply_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
void USBCOM_HandleISO0OUT(void)
|
||||||
|
{
|
||||||
|
__asm__("bkpt");
|
||||||
|
}
|
14
src/usb_com.h
Executable file
14
src/usb_com.h
Executable file
|
@ -0,0 +1,14 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "usb.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
USBCOM_REG_LEDCOUNT,
|
||||||
|
|
||||||
|
USBCOM_N_REGISTERS
|
||||||
|
} USBCOM_Register_t;
|
||||||
|
|
||||||
|
extern unsigned USBCOM_Registers[USBCOM_N_REGISTERS];
|
||||||
|
|
||||||
|
uint8_t USBCOM_HandleSetupPacket(USB_SetupPacket_t *sp, const uint8_t **reply_data);
|
||||||
|
void USBCOM_HandleISO0OUT(void);
|
75
src/usb_descriptors.c
Executable file
75
src/usb_descriptors.c
Executable file
|
@ -0,0 +1,75 @@
|
||||||
|
#include "usb_descriptors.h"
|
||||||
|
|
||||||
|
const USB_DeviceDescriptor_t USB_DeviceDescriptor =
|
||||||
|
{
|
||||||
|
.bLength = 18,
|
||||||
|
.bDescriptorType = USB_DEVICE_DESCRIPTOR,
|
||||||
|
.bcdUSB = 0x0200,
|
||||||
|
.bDeviceClass = 0xff,
|
||||||
|
.bDeviceSubClass = 0xff,
|
||||||
|
.bDeviceProtocol = 0x00,
|
||||||
|
.bMaxPacketSize0 = 64,
|
||||||
|
.idVendor = 0x16c0,
|
||||||
|
.idProduct = 0x05dc,
|
||||||
|
.bcdDevice = 0x0200,
|
||||||
|
.iManufacturer = 1,
|
||||||
|
.iProduct = 2,
|
||||||
|
.iSerialNumber = 3,
|
||||||
|
.bNumConfigurations = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
const USB_WholeDescriptor_t USB_ConfigurationInterfaceDescriptor =
|
||||||
|
{
|
||||||
|
.configuration = (USB_ConfigurationDescriptor_t)
|
||||||
|
{
|
||||||
|
.bLength = 9,
|
||||||
|
.bDescriptorType = USB_CONFIGURATION_DESCRIPTOR,
|
||||||
|
.wTotalLength = sizeof(USB_WholeDescriptor_t),
|
||||||
|
.bNumInterfaces = 1,
|
||||||
|
.bConfigurationValue = 1,
|
||||||
|
.iConfiguration = 0,
|
||||||
|
.bmAttributes = 0x80,
|
||||||
|
.bMaxPower = 100
|
||||||
|
},
|
||||||
|
.interface0 = (USB_InterfaceDescriptor_t)
|
||||||
|
{
|
||||||
|
.bLength = 9,
|
||||||
|
.bDescriptorType = USB_INTERFACE_DESCRIPTOR,
|
||||||
|
.bInterfaceNumber = 0,
|
||||||
|
.bAlternateSetting = 0,
|
||||||
|
.bNumEndpoints = 1,
|
||||||
|
.bInterfaceClass = 0,
|
||||||
|
.bInterfaceSubClass = 0,
|
||||||
|
.bInterfaceProtocol = 0,
|
||||||
|
.iInterface = 0
|
||||||
|
},
|
||||||
|
.endpoint0out = (USB_EndpointDescriptor_t)
|
||||||
|
{
|
||||||
|
.bLength = sizeof(USB_EndpointDescriptor_t),
|
||||||
|
.bDescriptorType = USB_ENDPOINT_DESCRIPTOR,
|
||||||
|
.bEndpointAddress = 1,
|
||||||
|
.bmAttributes = 1,
|
||||||
|
.wMaxPacketSize = 64,
|
||||||
|
.bInterval = 1
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define USB_STRING_LANGID 0x0409
|
||||||
|
#define USB_STRING_VENDOR 'F', 'r', 'u', 'c', 'h', 't', \
|
||||||
|
'i', '\'', 's', 0
|
||||||
|
#define USB_STRING_PRODUCT 'e', 'x', 't', 'r', 'e', 'm', \
|
||||||
|
'e', 'l', 'y', ' ', \
|
||||||
|
'd', 'u', 'm', 'b', ' ', \
|
||||||
|
't', 'h', 'e', 'r', 'm', 'a', \
|
||||||
|
'l', ' ', 'p', 'r', 'i', 'n', \
|
||||||
|
't', 'e', 'r', 0
|
||||||
|
#define USB_STRING_SERIAL 'v', '0', '.', '0', '0', '1', 0
|
||||||
|
|
||||||
|
const uint16_t USB_StringDescriptor_LangID[] =
|
||||||
|
USB_BUILD_STRING_DESCRIPTOR(USB_STRING_LANGID);
|
||||||
|
const uint16_t USB_StringDescriptor_Vendor[] =
|
||||||
|
USB_BUILD_STRING_DESCRIPTOR(USB_STRING_VENDOR);
|
||||||
|
const uint16_t USB_StringDescriptor_Product[] =
|
||||||
|
USB_BUILD_STRING_DESCRIPTOR(USB_STRING_PRODUCT);
|
||||||
|
const uint16_t USB_StringDescriptor_Serial[] =
|
||||||
|
USB_BUILD_STRING_DESCRIPTOR(USB_STRING_SERIAL);
|
106
src/usb_descriptors.h
Executable file
106
src/usb_descriptors.h
Executable file
|
@ -0,0 +1,106 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef union
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint8_t bLength;
|
||||||
|
uint8_t bDescriptorType;
|
||||||
|
uint16_t bcdUSB;
|
||||||
|
uint8_t bDeviceClass;
|
||||||
|
uint8_t bDeviceSubClass;
|
||||||
|
uint8_t bDeviceProtocol;
|
||||||
|
uint8_t bMaxPacketSize0;
|
||||||
|
uint16_t idVendor;
|
||||||
|
uint16_t idProduct;
|
||||||
|
uint16_t bcdDevice;
|
||||||
|
uint8_t iManufacturer;
|
||||||
|
uint8_t iProduct;
|
||||||
|
uint8_t iSerialNumber;
|
||||||
|
uint8_t bNumConfigurations;
|
||||||
|
};
|
||||||
|
uint8_t raw[18];
|
||||||
|
} __attribute__((packed)) USB_DeviceDescriptor_t;
|
||||||
|
|
||||||
|
typedef union
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint8_t bLength;
|
||||||
|
uint8_t bDescriptorType;
|
||||||
|
uint16_t wTotalLength;
|
||||||
|
uint8_t bNumInterfaces;
|
||||||
|
uint8_t bConfigurationValue;
|
||||||
|
uint8_t iConfiguration;
|
||||||
|
uint8_t bmAttributes;
|
||||||
|
uint8_t bMaxPower;
|
||||||
|
} __attribute__((packed, aligned(1)));
|
||||||
|
uint8_t raw[9];
|
||||||
|
} __attribute__((packed, aligned(1))) USB_ConfigurationDescriptor_t;
|
||||||
|
|
||||||
|
typedef union
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint8_t bLength;
|
||||||
|
uint8_t bDescriptorType;
|
||||||
|
uint8_t bInterfaceNumber;
|
||||||
|
uint8_t bAlternateSetting;
|
||||||
|
uint8_t bNumEndpoints;
|
||||||
|
uint8_t bInterfaceClass;
|
||||||
|
uint8_t bInterfaceSubClass;
|
||||||
|
uint8_t bInterfaceProtocol;
|
||||||
|
uint8_t iInterface;
|
||||||
|
} __attribute__((packed, aligned(1)));
|
||||||
|
uint8_t raw[9];
|
||||||
|
} __attribute__((packed, aligned(1))) USB_InterfaceDescriptor_t;
|
||||||
|
|
||||||
|
typedef union
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint8_t bLength;
|
||||||
|
uint8_t bDescriptorType;
|
||||||
|
uint8_t bEndpointAddress;
|
||||||
|
uint8_t bmAttributes;
|
||||||
|
uint16_t wMaxPacketSize;
|
||||||
|
uint8_t bInterval;
|
||||||
|
};
|
||||||
|
uint8_t raw[7];
|
||||||
|
} __attribute__((packed)) USB_EndpointDescriptor_t;
|
||||||
|
|
||||||
|
typedef union
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
USB_ConfigurationDescriptor_t configuration;
|
||||||
|
USB_InterfaceDescriptor_t interface0;
|
||||||
|
USB_EndpointDescriptor_t endpoint0out;
|
||||||
|
} __attribute__((packed, aligned(1)));
|
||||||
|
uint8_t raw[18];
|
||||||
|
} __attribute__((packed, aligned(1))) USB_WholeDescriptor_t;
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
USB_DEVICE_DESCRIPTOR = 1,
|
||||||
|
USB_CONFIGURATION_DESCRIPTOR,
|
||||||
|
USB_STRING_DESCRIPTOR,
|
||||||
|
USB_INTERFACE_DESCRIPTOR,
|
||||||
|
USB_ENDPOINT_DESCRIPTOR
|
||||||
|
} __attribute__((packed)) USB_DescriptorType_t;
|
||||||
|
|
||||||
|
#define USB_STRING_DESCRIPTOR_LENGTH(...) (sizeof((uint16_t[]){__VA_ARGS__}) + 2)
|
||||||
|
#define USB_BUILD_STRING_DESCRIPTOR(...) {USB_STRING_DESCRIPTOR_LENGTH(__VA_ARGS__) | (USB_STRING_DESCRIPTOR << 8), __VA_ARGS__}
|
||||||
|
|
||||||
|
extern const uint16_t USB_StringDescriptor_LangID[];
|
||||||
|
extern const uint16_t USB_StringDescriptor_Vendor[];
|
||||||
|
extern const uint16_t USB_StringDescriptor_Product[];
|
||||||
|
extern const uint16_t USB_StringDescriptor_Serial[];
|
||||||
|
|
||||||
|
extern const USB_DeviceDescriptor_t USB_DeviceDescriptor;
|
||||||
|
// extern const USB_ConfigurationDescriptor_t USB_ConfigurationDescriptor;
|
||||||
|
// extern const USB_InterfaceDescriptor_t USB_InterfaceDescriptor;
|
||||||
|
extern const USB_WholeDescriptor_t USB_ConfigurationInterfaceDescriptor;
|
Loading…
Reference in a new issue