commit c695969e599dceb913d5bf478cae5e1a401a7222 Author: fruchti Date: Sun Jul 29 18:24:52 2018 +0200 Add basic thermal printer control diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..b89ba96 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build/* +src/.local.vimrc diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100755 index 0000000..91a5228 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -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 +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100755 index 0000000..8335832 --- /dev/null +++ b/.vscode/launch.json @@ -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" + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100755 index 0000000..60bca49 --- /dev/null +++ b/.vscode/settings.json @@ -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" +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100755 index 0000000..1dc1014 --- /dev/null +++ b/.vscode/tasks.json @@ -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" + } + } + ] +} \ No newline at end of file diff --git a/build-number.txt b/build-number.txt new file mode 100644 index 0000000..fc902f4 --- /dev/null +++ b/build-number.txt @@ -0,0 +1 @@ +124 diff --git a/ld/STM32F103C8T6_FLASH.ld b/ld/STM32F103C8T6_FLASH.ld new file mode 100755 index 0000000..e6d6076 --- /dev/null +++ b/ld/STM32F103C8T6_FLASH.ld @@ -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) } +} + + diff --git a/makefile b/makefile new file mode 100755 index 0000000..13d0593 --- /dev/null +++ b/makefile @@ -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)/* diff --git a/src/buildid.h b/src/buildid.h new file mode 100755 index 0000000..8585be1 --- /dev/null +++ b/src/buildid.h @@ -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 diff --git a/src/debug.c b/src/debug.c new file mode 100755 index 0000000..284c446 --- /dev/null +++ b/src/debug.c @@ -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"); +} diff --git a/src/debug.h b/src/debug.h new file mode 100755 index 0000000..b60c86b --- /dev/null +++ b/src/debug.h @@ -0,0 +1,9 @@ +#pragma once + +#include +#include + +#include "stm32f1xx.h" + +void Debug_Print(const char *message); +void Debug_PutChar(char c); diff --git a/src/ltp1245.c b/src/ltp1245.c new file mode 100644 index 0000000..8a7a90d --- /dev/null +++ b/src/ltp1245.c @@ -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)); +} \ No newline at end of file diff --git a/src/ltp1245.h b/src/ltp1245.h new file mode 100644 index 0000000..332bf46 --- /dev/null +++ b/src/ltp1245.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +#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); diff --git a/src/main.c b/src/main.c new file mode 100755 index 0000000..73c9f17 --- /dev/null +++ b/src/main.c @@ -0,0 +1,12 @@ +#include "main.h" + +int main(void) +{ + USB_Init(); + LTP1245_Init(); + + for(;;) + { + } +} + diff --git a/src/main.h b/src/main.h new file mode 100755 index 0000000..7ef894c --- /dev/null +++ b/src/main.h @@ -0,0 +1,15 @@ +#ifndef MAIN_H_ +#define MAIN_H_ + +#include +#include "stm32f1xx.h" + +#include "pinning.h" +#include "buildid.h" +#include "debug.h" +#include "usb.h" +#include "ltp1245.h" + +int main(void); + +#endif diff --git a/src/pinning.h b/src/pinning.h new file mode 100755 index 0000000..d10221f --- /dev/null +++ b/src/pinning.h @@ -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 diff --git a/src/system.c b/src/system.c new file mode 100755 index 0000000..ddcfb14 --- /dev/null +++ b/src/system.c @@ -0,0 +1,32 @@ +#include +#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; +} + diff --git a/src/usb.c b/src/usb.c new file mode 100755 index 0000000..d975f74 --- /dev/null +++ b/src/usb.c @@ -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); + } + } + } +} \ No newline at end of file diff --git a/src/usb.h b/src/usb.h new file mode 100755 index 0000000..6f2803a --- /dev/null +++ b/src/usb.h @@ -0,0 +1,113 @@ +#pragma once + +#include +#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 = , wIndex = 0||, wLength = 0 +#define USB_REQUEST_GET_CONFIGURATION 8 // wValue = 0, wIndex = 0, wLength = 1 +#define USB_REQUEST_GET_DESCRIPTOR 6 // wValue = :, wIndex = 0|, wLength = +#define USB_REQUEST_GET_INTERFACE 10 // wValue = 0, wIndex = , wLength = 1 +#define USB_REQUEST_GET_STATUS 0 // wValue = 0, wIndex = 0||, wLength = 2 +#define USB_REQUEST_SET_ADDRESS 5 // wValue =
, wIndex = 0, wLength = 0 +#define USB_REQUEST_SET_CONFIGURATION 9 // wValue = , wIndex = 0, wLength = 0 +#define USB_REQUEST_SET_DESCRIPTOR 7 // wValue = :, wIndex = 0|, wLength = +#define USB_REQUEST_SET_FEATURE 3 // wValue = , wIndex = 0||, wLength = 0 +#define USB_REQUEST_SET_INTERFACE 11 // wValue = , wIndex = , wLength = 0 +#define USB_REQUEST_SYNCH_FRAME 12 // wValue = 0, wIndex = , 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); diff --git a/src/usb_com.c b/src/usb_com.c new file mode 100755 index 0000000..db71f1c --- /dev/null +++ b/src/usb_com.c @@ -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"); +} \ No newline at end of file diff --git a/src/usb_com.h b/src/usb_com.h new file mode 100755 index 0000000..59ed85e --- /dev/null +++ b/src/usb_com.h @@ -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); diff --git a/src/usb_descriptors.c b/src/usb_descriptors.c new file mode 100755 index 0000000..8269785 --- /dev/null +++ b/src/usb_descriptors.c @@ -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); \ No newline at end of file diff --git a/src/usb_descriptors.h b/src/usb_descriptors.h new file mode 100755 index 0000000..7bdd4dd --- /dev/null +++ b/src/usb_descriptors.h @@ -0,0 +1,106 @@ +#pragma once + +#include + +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;