Add basic thermal printer control

This commit is contained in:
fruchti 2018-07-29 18:24:52 +02:00
commit c695969e59
23 changed files with 1762 additions and 0 deletions

2
.gitignore vendored Executable file
View file

@ -0,0 +1,2 @@
build/*
src/.local.vimrc

29
.vscode/c_cpp_properties.json vendored Executable file
View 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
View 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
View 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
View 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
View file

@ -0,0 +1 @@
124

169
ld/STM32F103C8T6_FLASH.ld Executable file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View file

@ -0,0 +1,12 @@
#include "main.h"
int main(void)
{
USB_Init();
LTP1245_Init();
for(;;)
{
}
}

15
src/main.h Executable file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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;