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