Add Canon Lens management

This commit is contained in:
Edward Emelianov 2022-09-10 12:44:37 +03:00
parent bbf855b054
commit 8ffe8ab462
21 changed files with 2219 additions and 0 deletions

View File

@ -0,0 +1,151 @@
BINARY = canonusb
BOOTPORT ?= /dev/ttyUSB0
BOOTSPEED ?= 115200
# MCU FAMILY
FAMILY ?= F1
# MCU code
MCU ?= F103x8
# density (stm32f10x.h, lines 70-84)
DENSITY ?= LD
# change this linking script depending on particular MCU model,
LDSCRIPT ?= stm32f103x8.ld
# debug
#DEFS = -DEBUG
# autoincremental version & build date
VERSION_FILE = version.inc
NEXTVER := $(shell expr $$(awk '/#define BUILD_NUMBER/' $(VERSION_FILE) | tr -cd "[0-9]") + 1)
BUILDDATE := $(shell date +%Y-%m-%d)
INDEPENDENT_HEADERS=
FP_FLAGS ?= -msoft-float -mfloat-abi=soft
ASM_FLAGS ?= -mthumb -mcpu=cortex-m3 -mfix-cortex-m3-ldrd
ARCH_FLAGS = $(ASM_FLAGS) $(FP_FLAGS)
###############################################################################
# Executables
#PREFIX ?= arm-none-eabi
# gcc from arm web site
PREFIX ?= /opt/bin/arm-none-eabi
TOOLCHLIB ?= /opt/arm-none-eabi/lib
RM := rm -f
RMDIR := rmdir
CC := $(PREFIX)-gcc
LD := $(PREFIX)-gcc
AR := $(PREFIX)-ar
AS := $(PREFIX)-as
SIZE := $(PREFIX)-size
OBJCOPY := $(PREFIX)-objcopy
OBJDUMP := $(PREFIX)-objdump
GDB := $(PREFIX)-gdb
STFLASH := $(shell which st-flash)
STBOOT := $(shell which stm32flash)
DFUUTIL := $(shell which dfu-util)
###############################################################################
# Source files
OBJDIR := mk
SRC := $(wildcard *.c)
OBJS := $(addprefix $(OBJDIR)/, $(SRC:%.c=%.o))
STARTUP = $(OBJDIR)/startup.o
OBJS += $(STARTUP)
# dependencies: we need them to recompile files if their headers-dependencies changed
DEPS := $(OBJS:.o=.d)
INC_DIR ?= ../inc
INCLUDE := -I$(INC_DIR)/Fx -I$(INC_DIR)/cm
LIB_DIR := $(INC_DIR)/ld
###############################################################################
# C flags
CFLAGS += -O2 -g -D__thumb2__=1 -MD -g -gdwarf-2
CFLAGS += -Wall -Werror -Wextra -Wshadow
CFLAGS += -fno-common -ffunction-sections -fdata-sections -fno-stack-protector -fshort-enums
CFLAGS += $(ARCH_FLAGS)
###############################################################################
# Linker flags
LDFLAGS += -nostartfiles -nostdlib --static -Wl,--gc-sections -Wl,--print-memory-usage
LDFLAGS += -Wl,-Map=$(OBJDIR)/$(BINARY).map
#LDFLAGS += --static --gc-sections --print-memory-usage
LDFLAGS += -L$(LIB_DIR) -L$(TOOLCHLIB)
LDFLAGS += -T$(LDSCRIPT)
###############################################################################
# Used libraries
LDLIBS += -lc -lgcc $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
DEFS += -DSTM32$(FAMILY) -DSTM32$(MCU) -DSTM32F10X_$(DENSITY)
ELF := $(OBJDIR)/$(BINARY).elf
LIST := $(OBJDIR)/$(BINARY).list
BIN := $(BINARY).bin
HEX := $(BINARY).hex
all: bin list size
elf: $(ELF)
bin: $(BIN)
hex: $(HEX)
list: $(LIST)
ifneq ($(MAKECMDGOALS),clean)
-include $(DEPS)
endif
$(OBJDIR):
mkdir $(OBJDIR)
$(STARTUP): $(INC_DIR)/startup/vector.c
$(CC) $(CFLAGS) $(DEFS) $(INCLUDE) -o $@ -c $<
$(VERSION_FILE): *.[ch]
@echo " Generate version: $(NEXTVER) for date $(BUILDDATE)"
@sed -i "s/#define BUILD_NUMBER.*/#define BUILD_NUMBER \"$(NEXTVER)\"/" $(VERSION_FILE)
@sed -i "s/#define BUILD_DATE.*/#define BUILD_DATE \"$(BUILDDATE)\"/" $(VERSION_FILE)
$(OBJDIR)/proto.o: proto.c $(VERSION_FILE)
$(OBJDIR)/%.o: %.c
@echo " CC $<"
$(CC) $(CFLAGS) $(DEFS) $(INCLUDE) -o $@ -c $<
$(BIN): $(ELF)
@echo " OBJCOPY $(BIN)"
$(OBJCOPY) -Obinary $(ELF) $(BIN)
$(HEX): $(ELF)
@echo " OBJCOPY $(HEX)"
$(OBJCOPY) -Oihex $(ELF) $(HEX)
$(LIST): $(ELF)
@echo " OBJDUMP $(LIST)"
$(OBJDUMP) -S $(ELF) > $(LIST)
$(ELF): $(OBJDIR) $(OBJS)
@echo " LD $(ELF)"
$(LD) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $(ELF)
size: $(ELF)
$(SIZE) $(ELF)
clean:
@echo " CLEAN"
@rm -rf $(OBJDIR) 2>/dev/null || true
flash: $(BIN)
@echo " FLASH $(BIN)"
$(STFLASH) write $(BIN) 0x8000000
boot: $(BIN)
@echo " LOAD $(BIN) through bootloader"
$(STBOOT) -b$(BOOTSPEED) $(BOOTPORT) -w $(BIN)
dfuboot: $(BIN)
@echo " LOAD $(BIN) THROUGH DFU"
$(DFUUTIL) -a0 -D $(BIN) -s 0x08000000
.PHONY: clean flash boot

View File

@ -0,0 +1,24 @@
Canon lens management.
Protocol have a string form, each string ends with '\n'. You should wait an answer for previous command before sending next,
or have risk to miss all the rest commands in one packet.
USB commands:
0 - move to smallest foc value (e.g. 2.5m)
1 - move to largest foc value (e.g. infinity)
d - open/close diaphragm by 1 step (+/-), open/close fully (o/c) (no way to know it current status)
f - get focus state or move it to given relative position
h - turn on hand focus management
i - get lens information
l - get lens model
r - get regulators' state
debugging commands:
F - change SPI flags (F f val), f== l-LSBFIRST, b-BR [18MHz/2^(b+1)], p-CPOL, h-CPHA
G - get SPI status
I - reinit SPI
R - software reset
S - send data over SPI
T - show Tms value

View File

@ -0,0 +1,215 @@
/*
* This file is part of the canonmanage project.
* Copyright 2022 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "canon.h"
#include "hardware.h"
#include "proto.h"
#include "spi.h"
#include "usb.h"
#define CU(a) ((const uint8_t*)a)
#if 0
typedef struct{
canon_commands cmd; // command code
int zeros; // amount of zeros after command
} command;
static command commands[] = {
{CANON_LONGID, 11},
{CANON_ID, 6},
{CANON_REPDIA, 1},
{CANON_FSTOP, 1},
{CANON_FMIN, 1},
{CANON_FMAX, 1},
{CANON_POWERON, 1},
{CANON_POWEROFF, 1},
{CANON_POLL, 1},
{CANON_DIAPHRAGM, 2},
{CANON_FOCMOVE, 2},
{CANON_FOCBYHANDS, 1},
{CANON_GETINFO, 15},
{CANON_GETREG, 2},
{CANON_GETFOCLIM, 2},
{CANON_GETDIAL, 2},
{CANON_GETFOCM, 4},
};
#endif
// command buffer (buf[0] == last command sent)
static uint8_t buf[SPIBUFSZ] = {0}, ready = 0;
static void canon_read(uint8_t cmd, uint8_t zeroz){
if(zeroz > MAXCMDLEN - 1) return;
++zeroz;
*((uint32_t*)buf) = 0;
buf[0] = cmd;
SPI_transmit(buf, zeroz);
}/*
static void canon_writeu8(uint8_t cmd, uint8_t u){
*((uint32_t*)buf) = 0;
buf[0] = cmd; buf[1] = u;
SPI_transmit(buf, 2);
}*/
static void canon_writeu16(uint8_t cmd, uint16_t u){
*((uint32_t*)buf) = 0;
buf[0] = cmd; buf[1] = u >> 8; buf[2] = u & 0xff;
SPI_transmit(buf, 3);
}
static void canon_poll(){
ready = 0;
canon_read(CANON_POLL, 0);
}
// turn on power and send ack
void canon_init(){
ready = 0;
canon_read(CANON_ID, 31);
}
// send over USB 16-bit unsigned
static void printu16(uint8_t *b){
USB_send(u2str((b[1] << 8) | b[2]));
}
/**
* @brief canon_proc - check incoming SPI messages
*/
void canon_proc(){
uint8_t lastcmd = buf[0]; // last command sent
uint32_t uval;
uint8_t x;
uint8_t *rbuf = SPI_receive(&x);
if(!rbuf) return;
#ifdef EBUG
//if(lastcmd != CANON_POLL){
USB_send("SPI receive: ");
for(uint8_t i = 0; i < x; ++i){
if(i) USB_send(", ");
USB_send(u2hexstr(rbuf[i]));
}
USB_send("\n");
//}
#endif
int need2poll = 0;
switch (lastcmd){
case CANON_LONGID: // something
// need2poll = 0;
break;
case CANON_ID: // got ID -> turn on power
canon_read(CANON_POWERON, 1);
break;
/*case CANON_POWERON:
;
break;*/
case CANON_POLL:
if(rbuf[0] == CANON_POLLANS){
canon_read(CANON_LONGID, 0);
ready = 1;
#ifdef EBUG
USB_send("Ready!\n");
#endif
}else need2poll = 1;
break;
case CANON_GETINFO:
USB_send("Info="); for(int i = 1; i < 7; ++i){
USB_send(u2hexstr(rbuf[i])); USB_send(" ");
}
USB_send("\n");
break;
case CANON_GETREG:
USB_send("Reg="); USB_send(u2hexstr((rbuf[1] << 8) | rbuf[2])); USB_send("\n");
break;
case CANON_GETMODEL:
USB_send("Lens="); printu16(rbuf); USB_send("\n");
break;
case CANON_GETDIAL:
USB_send("Fsteps="); printu16(rbuf); USB_send("\n");
//canon_read(CANON_GETFOCM, 4);
break;
case CANON_GETFOCM: // don't work @EF200
uval = (rbuf[1] << 24) | (rbuf[2] << 16) | (rbuf[3] << 8) | rbuf[4];
USB_send("Fval="); USB_send(u2str(uval)); USB_send("\n");
break;
default:
need2poll = 1; // poll after any other command
break;
}
if(need2poll) canon_poll();
}
/**
* @brief canon_diaphragm - run comands
* @param command: open/close diaphragm by 1 step (+/-), open/close fully (o/c)
* @return 0 if success or error code (1 - not ready, 2 - bad command)
*/
int canon_diaphragm(char command){
if(!ready) return 1;
int16_t val = 0;
switch(command){
case '+':
val = -1;
break;
case '-':
val = 1;
break;
case 'o':
case 'O':
val = 128;
break;
case 'c':
case 'C':
val = 127;
break;
default:
return 2; // unknown command
}
canon_writeu16(CANON_DIAPHRAGM, (uint16_t)(val << 8));
return 0;
}
int canon_focus(int16_t val){
if(!ready) return 1;
if(val == 0) canon_read(CANON_GETDIAL, 2);
else canon_writeu16(CANON_FOCMOVE, val);
return 0;
}
int canon_sendcmd(uint8_t cmd){
if(!ready) return 1;
canon_read(cmd, 0);
return 0;
}
void canon_setlastcmd(uint8_t x){
buf[0] = x;
}
// acquire 16bit value
int canon_asku16(uint8_t cmd){
if(!ready) return 1;
canon_read(cmd, 2);
return 0;
}
int canon_getinfo(){
if(!ready) return 1;
canon_read(CANON_GETINFO, 6);
return 0;
}

View File

@ -0,0 +1,58 @@
/*
* This file is part of the canonmanage project.
* Copyright 2022 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stm32f1.h>
// all data sent in big-endian format
// max length of commands
#define MAXCMDLEN (32)
// answer for cmd "POLL"
#define CANON_POLLANS (0xaa)
typedef enum{
CANON_LONGID = 0x00, // long ID ???
CANON_ID = 0x01, // lens ID and other info ???
CANON_REPDIA = 0x02, // repeat last diaphragm change
CANON_FSTOP = 0x04, // stop focus changing
CANON_FMAX = 0x05, // set Foc to max
CANON_FMIN = 0x06, // =//= min
CANON_POWERON = 0x07, // turn on motors' power
CANON_POWEROFF = 0x08, // turn off power
CANON_POLL = 0x0a, // bysu poll (ans 0xaa when ready or last command code)
CANON_DIAPHRAGM = 0x13, // open/close diaphragm by given (int8_t) value of steps
CANON_FOCMOVE = 0x44, // move focus dial by given amount of steps (int16)
CANON_FOCBYHANDS= 0x5e, // turn on focus move by hands (to turn off send 4,5 or 6)
CANON_GETINFO = 0x80, // get information
CANON_GETREG = 0x90, // get regulators' state
CANON_GETMODEL = 0xa0, // get lens (e.g. 200 == LX200)
CANON_GETDIAL = 0xc0, // get focus dial position in steps
CANON_GETFOCM = 0xc2, // get focus position in meters (not for all lenses)
} canon_commands;
void canon_init();
void canon_proc();
int canon_diaphragm(char command);
int canon_focus(int16_t val);
int canon_sendcmd(uint8_t cmd);
int canon_asku16(uint8_t cmd);
void canon_setlastcmd(uint8_t x);
int canon_getinfo();

Binary file not shown.

View File

@ -0,0 +1,39 @@
/*
* This file is part of the canonmanage project.
* Copyright 2022 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "hardware.h"
static inline void gpio_setup(){
// Set APB2 clock to 72/4=18MHz
RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_PPRE2) | RCC_CFGR_PPRE2_DIV4;
// Enable clocks to the GPIO subsystems, turn on AFIO clocking to disable SWD/JTAG
RCC->APB2ENR = RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_AFIOEN;
AFIO->MAPR = AFIO_MAPR_SWJ_CFG_JTAGDISABLE; // for PA15 - USB pullup
// Set led as opendrain output
GPIOC->CRH = CRH(13, CNF_ODOUTPUT | MODE_SLOW);
// setup SPI GPIO - alternate function PP (PA5 - SCK, PA6 - MISO, PA7 - MOSI)
GPIOA->CRL = CRL(5, CNF_AFPP|MODE_FAST) | CRL(6, CNF_FLINPUT) | CRL(7, CNF_AFPP|MODE_FAST);
// USB pullup (PA15) - pushpull output
USBPU_OFF();
GPIOA->CRH = CRH(15, CNF_PPOUTPUT | MODE_SLOW);
}
void hw_setup(){
gpio_setup();
}

View File

@ -0,0 +1,56 @@
/*
* This file is part of the canonmanage project.
* Copyright 2022 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __HARDWARE_H__
#define __HARDWARE_H__
#include <stm32f1.h>
// LED0 - PC13 (bluepill), blinking each second
#define LED0_port GPIOC
#define LED0_pin (1<<13)
#define LED_blink(x) pin_toggle(x ## _port, x ## _pin)
#define LED_on(x) pin_clear(x ## _port, x ## _pin)
#define LED_off(x) pin_set(x ## _port, x ## _pin)
#define USBPU_port GPIOA
#define USBPU_pin (1<<15)
#define USBPU_ON() pin_set(USBPU_port, USBPU_pin)
#define USBPU_OFF() pin_clear(USBPU_port, USBPU_pin)
extern volatile uint32_t Tms;
void hw_setup();
// SPI RX/TX max len
#define SPIBUFSZ (32)
#define DMA_SPI_Rx_IRQ DMA1_Channel2_IRQn
#define SPIx SPI1
#define SPI_APB2 RCC_APB2ENR_SPI1EN
#define DMA_SPI DMA1
#define DMA_SPI_AHBENR RCC_AHBENR_DMA1EN
#define DMA_SPI_TCIF DMA_ISR_TCIF2
#define DMA_SPI_CTCIF DMA_IFCR_CTCIF2
#define DMA_SPI_TxChannel DMA1_Channel3
#define DMA_SPI_RxChannel DMA1_Channel2
#define DMA_SPI_Rx_ISR dma1_channel2_isr
#endif // __HARDWARE_H__

View File

@ -0,0 +1,79 @@
/*
* This file is part of the canonmanage project.
* Copyright 2022 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "canon.h"
#include "hardware.h"
#include "proto.h"
#include "spi.h"
#include "usb.h"
#define USBBUFSZ 127
volatile uint32_t Tms = 0;
void sys_tick_handler(void){
++Tms;
}
// usb getline
char *get_USB(){
static char tmpbuf[USBBUFSZ+1], *curptr = tmpbuf;
static int rest = USBBUFSZ;
uint8_t x = USB_receive(curptr);
if(!x) return NULL;
curptr[x] = 0;
if(curptr[x-1] == '\n'){
curptr = tmpbuf;
rest = USBBUFSZ;
return tmpbuf;
}
curptr += x; rest -= x;
if(rest <= 0){ // buffer overflow
curptr = tmpbuf;
rest = USBBUFSZ;
}
return NULL;
}
int main(void){
sysreset();
StartHSE();
SysTick_Config(72000);
hw_setup();
USB_setup();
spi_setup();
canon_init();
uint32_t ctr = Tms, SPIctr = Tms;
while(1){
if(Tms - ctr > 499){
ctr = Tms;
LED_blink(LED0);
}
char *txt = NULL;
usb_proc();
if((txt = get_USB())){
const char *ans = parse_cmd(txt);
if(ans) USB_send(ans);
}
if(Tms != SPIctr){ // not more than once per 1ms
SPIctr = Tms;
canon_proc();
}
}
}

View File

@ -0,0 +1,378 @@
/*
* This file is part of the canonmanage project.
* Copyright 2022 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "canon.h"
#include "hardware.h"
#include "proto.h"
#include "spi.h"
#include "usb.h"
#include "version.inc"
char *omit_spaces(const char *buf){
while(*buf){
if(*buf > ' ') break;
++buf;
}
return (char*)buf;
}
// In case of overflow return `buf` and N==0xffffffff
// read decimal number & return pointer to next non-number symbol
static char *getdec(const char *buf, uint32_t *N){
char *start = (char*)buf;
uint32_t num = 0;
while(*buf){
char c = *buf;
if(c < '0' || c > '9'){
break;
}
if(num > 429496729 || (num == 429496729 && c > '5')){ // overflow
*N = 0xffffff;
return start;
}
num *= 10;
num += c - '0';
++buf;
}
*N = num;
return (char*)buf;
}
// read hexadecimal number (without 0x prefix!)
static char *gethex(const char *buf, uint32_t *N){
char *start = (char*)buf;
uint32_t num = 0;
while(*buf){
char c = *buf;
uint8_t M = 0;
if(c >= '0' && c <= '9'){
M = '0';
}else if(c >= 'A' && c <= 'F'){
M = 'A' - 10;
}else if(c >= 'a' && c <= 'f'){
M = 'a' - 10;
}
if(M){
if(num & 0xf0000000){ // overflow
*N = 0xffffff;
return start;
}
num <<= 4;
num += c - M;
}else{
break;
}
++buf;
}
*N = num;
return (char*)buf;
}
// read octal number (without 0 prefix!)
static char *getoct(const char *buf, uint32_t *N){
char *start = (char*)buf;
uint32_t num = 0;
while(*buf){
char c = *buf;
if(c < '0' || c > '7'){
break;
}
if(num & 0xe0000000){ // overflow
*N = 0xffffff;
return start;
}
num <<= 3;
num += c - '0';
++buf;
}
*N = num;
return (char*)buf;
}
// read binary number (without b prefix!)
static char *getbin(const char *buf, uint32_t *N){
char *start = (char*)buf;
uint32_t num = 0;
while(*buf){
char c = *buf;
if(c < '0' || c > '1'){
break;
}
if(num & 0x80000000){ // overflow
*N = 0xffffff;
return start;
}
num <<= 1;
if(c == '1') num |= 1;
++buf;
}
*N = num;
return (char*)buf;
}
/**
* @brief getnum - read uint32_t from string (dec, hex or bin: 127, 0x7f, 0b1111111)
* @param buf - buffer with number and so on
* @param N - the number read
* @return pointer to first non-number symbol in buf
* (if it is == buf, there's no number or if *N==0xffffffff there was overflow)
*/
char *getnum(const char *txt, uint32_t *N){
char *nxt = NULL;
char *s = omit_spaces(txt);
if(*s == '0'){ // hex, oct or 0
if(s[1] == 'x' || s[1] == 'X'){ // hex
nxt = gethex(s+2, N);
if(nxt == s+2) nxt = (char*)txt;
}else if(s[1] > '0'-1 && s[1] < '8'){ // oct
nxt = getoct(s+1, N);
if(nxt == s+1) nxt = (char*)txt;
}else{ // 0
nxt = s+1;
*N = 0;
}
}else if(*s == 'b' || *s == 'B'){
nxt = getbin(s+1, N);
if(nxt == s+1) nxt = (char*)txt;
}else{
nxt = getdec(s, N);
if(nxt == s) nxt = (char*)txt;
}
return nxt;
}
const char* helpmsg =
"https://github.com/eddyem/stm32samples/tree/master/F1-nolib/USB_Canon_management build#" BUILD_NUMBER " @ " BUILD_DATE "\n"
"0 - move to smallest foc value (e.g. 2.5m)\n"
"1 - move to largest foc value (e.g. infinity)\n"
"d - open/close diaphragm by 1 step (+/-), open/close fully (o/c) (no way to know it current status)\n"
"f - get focus state or move it to given relative position\n"
"h - turn on hand focus management\n"
"i - get lens information\n"
"l - get lens model\n"
"r - get regulators' state\n"
"\t\tdebugging commands:\n"
"F - change SPI flags (F f val), f== l-LSBFIRST, b-BR [18MHz/2^(b+1)], p-CPOL, h-CPHA\n"
"G - get SPI status\n"
"I - reinit SPI\n"
"R - software reset\n"
"S - send data over SPI\n"
"T - show Tms value\n"
;
#define STBUFSZ 255
static char stbuf[STBUFSZ+1], *bptr = NULL;
static int blen = 0;
static void initbuf(){bptr = stbuf; blen = STBUFSZ; *bptr = 0;}
#define newline() do{if(blen){ *bptr++ = '\n'; *bptr = 0; --blen; }}while(0)
static void add2buf(const char *s){
while(blen && *s){
*bptr++ = *s++;
--blen;
}
*bptr = 0;
}
static void errw(int e){
if(e){
add2buf("Error with code ");
add2buf(u2str(e));
if(e == 1) add2buf(" (busy or need initialization)");
}else add2buf("OK");
}
extern uint8_t usbON;
const char *parse_cmd(const char *buf){
//uint32_t u3;
initbuf();
if(buf[1] == '\n' || !buf[1]){ // one symbol commands
switch(*buf){
case '0':
errw(canon_sendcmd(CANON_FMIN));
break;
case '1':
errw(canon_sendcmd(CANON_FMAX));
break;
case 'f':
errw(canon_focus(0));
break;
case 'i':
errw(canon_getinfo());
break;
case 'l':
errw(canon_asku16(CANON_GETMODEL));
break;
case 'r':
errw(canon_asku16(CANON_GETREG));
break;
case 'F': // just watch SPI->CR1 value
add2buf("SPI1->CR1="); add2buf(u2hexstr(SPI_CR1));
break;
case 'G':
add2buf("SPI ");
switch(SPI_status){
case SPI_NOTREADY:
add2buf("not ready");
break;
case SPI_READY:
add2buf("ready");
break;
case SPI_BUSY:
add2buf("busy");
break;
default:
add2buf("unknown");
}
break;
case 'h':
errw(canon_sendcmd(CANON_FOCBYHANDS));
break;
case 'I':
add2buf("Reinit SPI");
spi_setup();
canon_init();
break;
case 'R':
USB_send("Soft reset\n");
NVIC_SystemReset();
break;
case 'T':
add2buf("Tms=");
add2buf(u2str(Tms));
break;
default:
return helpmsg;
}
newline();
return stbuf;
}
uint32_t D = 0, N = 0;
char *nxt;
switch(*buf){ // long messages
case 'd':
nxt = omit_spaces(buf+1);
errw(canon_diaphragm(*nxt));
break;
case 'f': // move focus
buf = omit_spaces(buf + 1);
int16_t neg = 1;
if(*buf == '-'){ ++buf; neg = -1; }
nxt = getnum(buf, &D);
if(nxt == buf) add2buf("Need number");
else if(D > 0x7fff) add2buf("From -0x7fff to 0x7fff");
else errw(canon_focus(neg * (int32_t)D));
break;
case 'F': // SPI flags
nxt = omit_spaces(buf+1);
char c = *nxt;
if(*nxt && *nxt != '\n'){
buf = nxt + 1;
nxt = getnum(buf, &D);
if(buf == nxt || D > 7) return helpmsg;
}
switch(c){
case 'b':
SPI_CR1 &= ~SPI_CR1_BR;
SPI_CR1 |= ((uint8_t)D) << 3;
break;
case 'h':
if(D) SPI_CR1 |= SPI_CR1_CPHA;
else SPI_CR1 &= ~SPI_CR1_CPHA;
break;
case 'l':
if(D) SPI_CR1 |= SPI_CR1_LSBFIRST;
else SPI_CR1 &= ~SPI_CR1_LSBFIRST;
break;
case 'p':
if(D) SPI_CR1 |= SPI_CR1_CPOL;
else SPI_CR1 &= ~SPI_CR1_CPOL;
break;
default:
return helpmsg;
}
add2buf("SPI_CR1="); add2buf(u2hexstr(SPI_CR1));
break;
case 'S': // use stbuf here to store user data
++buf;
do{
nxt = getnum(buf, &D);
if(buf == nxt) break;
buf = nxt;
if(D > 0xff){
USB_send("Number should be from 0 to 0xff\n");
return NULL;
}
stbuf[N++] = (uint8_t)D;
if(N == STBUFSZ) break;
}while(1);
if(N == 0){
USB_send("Enter data bytes\n");
return NULL;
}
USB_send("Send: ");
for(uint32_t i = 0; i < N; ++i){
if(i) USB_send(", ");
USB_send(u2hexstr(stbuf[i]));
}
USB_send("\n... ");
canon_setlastcmd(stbuf[0]);
if(N == SPI_transmit((uint8_t*)stbuf, (uint8_t)N)) USB_send("OK\n");
else USB_send("Failed\n");
return NULL;
break;
default:
return buf;
}
newline();
return stbuf;
}
// return string with number `val`
char *u2str(uint32_t val){
static char strbuf[11];
char *bufptr = &strbuf[10];
*bufptr = 0;
if(!val){
*(--bufptr) = '0';
}else{
while(val){
*(--bufptr) = val % 10 + '0';
val /= 10;
}
}
return bufptr;
}
char *u2hexstr(uint32_t val){
static char strbuf[11] = "0x";
char *sptr = strbuf + 2;
uint8_t *ptr = (uint8_t*)&val + 3;
int8_t i, j, z=1;
for(i = 0; i < 4; ++i, --ptr){
if(*ptr == 0){ // omit leading zeros
if(i == 3) z = 0;
if(z) continue;
}
else z = 0;
for(j = 1; j > -1; --j){
uint8_t half = (*ptr >> (4*j)) & 0x0f;
if(half < 10) *sptr++ = half + '0';
else *sptr++ = half - 10 + 'a';
}
}
*sptr = 0;
return strbuf;
}

View File

@ -0,0 +1,31 @@
/*
* This file is part of the canonmanage project.
* Copyright 2022 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef PROTO_H__
#define PROTO_H__
#include <stm32f1.h>
const char *parse_cmd(const char *buf);
char *omit_spaces(const char *buf);
char *getnum(const char *buf, uint32_t *N);
char *u2str(uint32_t val);
char *u2hexstr(uint32_t val);
#endif // PROTO_H__

View File

@ -0,0 +1,77 @@
/*
* This file is part of the canonmanage project.
* Copyright 2022 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stm32f1.h>
#include "ringbuffer.h"
// ring buffer
static char ringbuffer[RBSIZE];
// head - position of first data byte
// tail - position of last data byte + 1
// head == tail - empty! So, buffer can't store more than RBSIZE-1 bytes of data!
static volatile int head = 0, tail = 0;
static int datalen(){
if(tail >= head) return (tail - head);
else return (RBSIZE - head + tail);
}
static int restlen(){
return (RBSIZE - 1 - datalen());
}
static void mcpy(char *targ, const char *src, int l){
while(l--) *targ++ = *src++;
}
TRUE_INLINE void incr(volatile int *what, int n){
*what += n;
if(*what >= RBSIZE) *what -= RBSIZE;
}
int RB_read(char s[BLOCKSIZE]){
int l = datalen();
if(!l) return 0;
if(l > BLOCKSIZE) l = BLOCKSIZE;
int _1st = RBSIZE - head;
if(_1st > l) _1st = l;
if(_1st > BLOCKSIZE) _1st = BLOCKSIZE;
mcpy(s, ringbuffer+head, _1st);
if(_1st < BLOCKSIZE && l > _1st){
mcpy(s+_1st, ringbuffer, l-_1st);
incr(&head, l);
return l;
}
incr(&head ,_1st);
return _1st;
}
int RB_write(const char *str, int l){
int r = restlen();
if(l > r) l = r;
if(!l) return 0;
int _1st = RBSIZE - tail;
if(_1st > l) _1st = l;
mcpy(ringbuffer+tail, str, _1st);
if(_1st < l){ // add another piece from start
mcpy(ringbuffer, str+_1st, l-_1st);
}
incr(&tail, l);
return l;
}

View File

@ -0,0 +1,33 @@
/*
* This file is part of the canonmanage project.
* Copyright 2022 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef RINGBUFFER_H__
#define RINGBUFFER_H__
#include "usbhw.h" // for USB_TXBUFSZ
// ring buffer size in bytes
#define RBSIZE (512)
// max reading portion size
#define BLOCKSIZE (USB_TXBUFSZ)
int RB_read(char s[BLOCKSIZE]);
int RB_write(const char *str, int l);
#endif // RINGBUFFER_H__

View File

@ -0,0 +1,80 @@
/*
* This file is part of the LED_screen project.
* Copyright 2019 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// memcpy hangs -> use my own
#include "spi.h"
#include "hardware.h"
#ifdef EBUG
#include "usb.h"
#endif
#include "proto.h"
/*
static void mymemcpy(uint8_t *dest, uint8_t *src, int len){
while(len--) *dest++ = *src++;
}*/
// CR1 register default values, can be changed in 'proto.c'
uint32_t SPI_CR1 = SPI_CR1_MSTR | SPI_CR1_BR | SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_CPHA | SPI_CR1_CPOL;
spiStatus SPI_status = SPI_NOTREADY;
static uint8_t inbuff[SPIBUFSZ], lastlen = 0;
void spi_setup(){
RCC->APB2ENR |= SPI_APB2; // Enable the peripheral clock SPI1
// master, no slave select, BR=F/16, CPOL/CPHA - polarity.
SPIx->CR1 = SPI_CR1;
SPI_status = SPI_READY;
SPIx->CR1 |= SPI_CR1_SPE; // enable SPI
}
/**
* @brief SPI_transmit - transmit data over SPI DMA
* @param buf - data to transmit
* @param len - its length
* @return amount of transmitted data
*/
uint8_t SPI_transmit(const uint8_t *buf, uint8_t len){
if(!buf || !len) return 0; // bad data format
if(SPI_status != SPI_READY) return 0; // spi not ready to transmit data
for(uint8_t x = 0; x < len; ++x){
while(!(SPI1->SR & SPI_SR_TXE));
SPI1->DR = buf[x];
while(!(SPI1->SR & SPI_SR_BSY));
while(!(SPI1->SR & SPI_SR_RXNE));
inbuff[x] = SPI1->DR;
for(int ctr = 0; ctr < 3600; ++ctr) nop(); // ~100mks delay
}
lastlen = len;
return len;
}
/**
* @brief SPI_receive - get received data
* @param len (o) - received length
* @return received buffer
*/
uint8_t *SPI_receive(uint8_t *len){
if(SPI_status != SPI_READY) return NULL;
if(lastlen == 0) return NULL;
if(len) *len = lastlen;
lastlen = 0;
return inbuff;
}

View File

@ -0,0 +1,39 @@
/*
* This file is part of the LED_screen project.
* Copyright 2019 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef SPI_H__
#define SPI_H__
#include "stm32f1.h"
extern uint32_t SPI_CR1;
typedef enum{
SPI_NOTREADY,
SPI_READY,
SPI_BUSY
} spiStatus;
extern spiStatus SPI_status;
void spi_setup();
uint8_t SPI_transmit(const uint8_t *buf, uint8_t len);
uint8_t *SPI_receive(uint8_t *len);
#endif // SPI_H__

View File

@ -0,0 +1,116 @@
/*
* This file is part of the MLX90640 project.
* Copyright 2022 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ringbuffer.h"
#include "usb.h"
#include "usb_lib.h"
static char usbbuff[USB_TXBUFSZ]; // temporary buffer for sending data
volatile uint8_t tx_succesfull = 1;
static volatile uint8_t rxNE = 0;
void send_next(){
//if(!tx_succesfull) return;
static int lastdsz = 0;
int buflen = RB_read(usbbuff);
if(!buflen){
if(lastdsz == 64) EP_Write(3, NULL, 0); // send ZLP after 64 bits packet when nothing more to send
lastdsz = 0;
return;
}
tx_succesfull = 0;
EP_Write(3, (uint8_t*)usbbuff, buflen);
lastdsz = buflen;
}
// put `buf` into queue to send
void USB_send(const char *buf){
if(!buf || !usbON) return;
int len = 0;
const char *b = buf;
while(*b++) ++len;
if(!usbON || !len) return;
int l = len;
while(l){
if(tx_succesfull) send_next();
int a = RB_write(buf, l);
l -= a;
buf += a;
}
}
// interrupt IN handler (never used?)
static void EP1_Handler(){
uint16_t epstatus = KEEP_DTOG(USB->EPnR[1]);
if(RX_FLAG(epstatus)) epstatus = (epstatus & ~USB_EPnR_STAT_TX) ^ USB_EPnR_STAT_RX; // set valid RX
else epstatus = epstatus & ~(USB_EPnR_STAT_TX|USB_EPnR_STAT_RX);
// clear CTR
epstatus = (epstatus & ~(USB_EPnR_CTR_RX|USB_EPnR_CTR_TX));
USB->EPnR[1] = epstatus;
}
// data IN/OUT handlers
static void transmit_Handler(){ // EP3IN
tx_succesfull = 1;
uint16_t epstatus = KEEP_DTOG_STAT(USB->EPnR[3]);
// clear CTR keep DTOGs & STATs
USB->EPnR[3] = (epstatus & ~(USB_EPnR_CTR_TX)); // clear TX ctr
}
static void receive_Handler(){ // EP2OUT
rxNE = 1;
uint16_t epstatus = KEEP_DTOG_STAT(USB->EPnR[2]);
USB->EPnR[2] = (epstatus & ~(USB_EPnR_CTR_RX)); // clear RX ctr
}
void usb_proc(){
switch(USB_Dev.USB_Status){
case USB_STATE_CONFIGURED:
// make new BULK endpoint
// Buffer have 1024 bytes, but last 256 we use for CAN bus (30.2 of RM: USB main features)
EP_Init(1, EP_TYPE_INTERRUPT, USB_EP1BUFSZ, 0, EP1_Handler); // IN1 - transmit
EP_Init(2, EP_TYPE_BULK, 0, USB_RXBUFSZ, receive_Handler); // OUT2 - receive data
EP_Init(3, EP_TYPE_BULK, USB_TXBUFSZ, 0, transmit_Handler); // IN3 - transmit data
USB_Dev.USB_Status = USB_STATE_CONNECTED;
break;
case USB_STATE_DEFAULT:
case USB_STATE_ADDRESSED:
if(usbON){
usbON = 0;
}
break;
default: // USB_STATE_CONNECTED - send next data portion
if(!usbON) return;
if(tx_succesfull) send_next();
}
}
/**
* @brief USB_receive
* @param buf (i) - buffer[64] for received data
* @return amount of received bytes
*/
uint8_t USB_receive(char *buf){
if(!usbON || !rxNE) return 0;
uint8_t sz = EP_Read(2, (uint16_t*)buf);
uint16_t epstatus = KEEP_DTOG(USB->EPnR[2]);
// keep stat_tx & set ACK rx
USB->EPnR[2] = (epstatus & ~(USB_EPnR_STAT_TX)) ^ USB_EPnR_STAT_RX;
rxNE = 0;
return sz;
}

View File

@ -0,0 +1,34 @@
/*
* This file is part of the MLX90640 project.
* Copyright 2022 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __USB_H__
#define __USB_H__
#include "usbhw.h"
#define BUFFSIZE (64)
extern volatile uint8_t tx_succesfull;
void usb_proc();
void send_next();
void USB_send(const char *buf);
uint8_t USB_receive(char *buf);
#endif // __USB_H__

View File

@ -0,0 +1,387 @@
/*
* This file is part of the MLX90640 project.
* Copyright 2022 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include "usb_lib.h"
ep_t endpoints[STM32ENDPOINTS];
usb_dev_t USB_Dev;
static usb_LineCoding lineCoding = {115200, 0, 0, 8};
config_pack_t setup_packet;
uint8_t ep0databuf[EP0DATABUF_SIZE];
uint8_t ep0dbuflen = 0;
usb_LineCoding getLineCoding(){return lineCoding;}
uint8_t usbON = 0; // device disconnected from terminal
// definition of parts common for USB_DeviceDescriptor & USB_DeviceQualifierDescriptor
#define bcdUSB_L 0x10
#define bcdUSB_H 0x01
#define bDeviceClass 0
#define bDeviceSubClass 0
#define bDeviceProtocol 0
#define bNumConfigurations 1
static const uint8_t USB_DeviceDescriptor[] = {
18, // bLength
0x01, // bDescriptorType - Device descriptor
bcdUSB_L, // bcdUSB_L - 1.10
bcdUSB_H, // bcdUSB_H
bDeviceClass, // bDeviceClass - USB_COMM
bDeviceSubClass, // bDeviceSubClass
bDeviceProtocol, // bDeviceProtocol
USB_EP0_BUFSZ, // bMaxPacketSize
0x7b, // idVendor_L PL2303: VID=0x067b, PID=0x2303
0x06, // idVendor_H
0x03, // idProduct_L
0x23, // idProduct_H
0x00, // bcdDevice_Ver_L
0x03, // bcdDevice_Ver_H
0x01, // iManufacturer
0x02, // iProduct
0x00, // iSerialNumber
bNumConfigurations // bNumConfigurations
};
static const uint8_t USB_DeviceQualifierDescriptor[] = {
10, //bLength
0x06, // bDescriptorType - Device qualifier
bcdUSB_L, // bcdUSB_L
bcdUSB_H, // bcdUSB_H
bDeviceClass, // bDeviceClass
bDeviceSubClass, // bDeviceSubClass
bDeviceProtocol, // bDeviceProtocol
USB_EP0_BUFSZ, // bMaxPacketSize0
bNumConfigurations, // bNumConfigurations
0x00 // Reserved
};
static const uint8_t USB_ConfigDescriptor[] = {
/*Configuration Descriptor*/
0x09, /* bLength: Configuration Descriptor size */
0x02, /* bDescriptorType: Configuration */
39, /* wTotalLength:no of returned bytes */
0x00,
0x01, /* bNumInterfaces: 1 interface */
0x01, /* bConfigurationValue: Configuration value */
0x00, /* iConfiguration: Index of string descriptor describing the configuration */
0xa0, /* bmAttributes - Bus powered, Remote wakeup */
0x32, /* MaxPower 100 mA */
/*---------------------------------------------------------------------------*/
/*Interface Descriptor */
0x09, /* bLength: Interface Descriptor size */
0x04, /* bDescriptorType: Interface */
0x00, /* bInterfaceNumber: Number of Interface */
0x00, /* bAlternateSetting: Alternate setting */
0x03, /* bNumEndpoints: 3 endpoints used */
0xff, /* bInterfaceClass */
0x00, /* bInterfaceSubClass */
0x00, /* bInterfaceProtocol */
0x00, /* iInterface: */
///////////////////////////////////////////////////
/*Endpoint 1 Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
0x05, /* bDescriptorType: Endpoint */
0x81, /* bEndpointAddress IN1 */
0x03, /* bmAttributes: Interrupt */
0x0a, /* wMaxPacketSize LO: */
0x00, /* wMaxPacketSize HI: */
0x01, /* bInterval: */
/*Endpoint OUT2 Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
0x05, /* bDescriptorType: Endpoint */
0x02, /* bEndpointAddress: OUT2 */
0x02, /* bmAttributes: Bulk */
(USB_RXBUFSZ & 0xff), /* wMaxPacketSize: 64 */
(USB_RXBUFSZ >> 8),
0x00, /* bInterval: ignore for Bulk transfer */
/*Endpoint IN3 Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
0x05, /* bDescriptorType: Endpoint */
0x83, /* bEndpointAddress IN3 */
0x02, /* bmAttributes: Bulk */
(USB_TXBUFSZ & 0xff), /* wMaxPacketSize: 64 */
(USB_TXBUFSZ >> 8),
0x00, /* bInterval: ignore for Bulk transfer */
};
_USB_LANG_ID_(USB_StringLangDescriptor, LANG_US);
// these descriptors are not used in PL2303 emulator!
_USB_STRING_(USB_StringSerialDescriptor, u"0");
_USB_STRING_(USB_StringManufacturingDescriptor, u"Prolific Technology Inc.");
_USB_STRING_(USB_StringProdDescriptor, u"USB-Serial Controller");
/*
* default handlers
*/
// SET_LINE_CODING
void WEAK linecoding_handler(usb_LineCoding __attribute__((unused)) *lc){
}
// SET_CONTROL_LINE_STATE
void WEAK clstate_handler(uint16_t __attribute__((unused)) val){
}
// SEND_BREAK
void WEAK break_handler(){
}
// handler of vendor requests
void WEAK vendor_handler(config_pack_t *packet){
if(packet->bmRequestType & 0x80){ // read
uint8_t c;
switch(packet->wValue){
case 0x8484:
c = 2;
break;
case 0x0080:
c = 1;
break;
case 0x8686:
c = 0xaa;
break;
default:
c = 0;
}
EP_WriteIRQ(0, &c, 1);
}else{ // write ZLP
EP_WriteIRQ(0, (uint8_t *)0, 0);
}
}
static void wr0(const uint8_t *buf, uint16_t size){
if(setup_packet.wLength < size) size = setup_packet.wLength; // shortened request
if(size < endpoints[0].txbufsz){
EP_WriteIRQ(0, buf, size);
return;
}
while(size){
uint16_t l = size;
if(l > endpoints[0].txbufsz) l = endpoints[0].txbufsz;
EP_WriteIRQ(0, buf, l);
buf += l;
size -= l;
uint8_t needzlp = (l == endpoints[0].txbufsz) ? 1 : 0;
if(size || needzlp){ // send last data buffer
uint16_t status = KEEP_DTOG(USB->EPnR[0]);
// keep DTOGs, clear CTR_RX,TX, set TX VALID, leave stat_Rx
USB->EPnR[0] = (status & ~(USB_EPnR_CTR_RX|USB_EPnR_CTR_TX|USB_EPnR_STAT_RX))
^ USB_EPnR_STAT_TX;
uint32_t ctr = 1000000;
while(--ctr && (USB->ISTR & USB_ISTR_CTR) == 0){IWDG->KR = IWDG_REFRESH;};
if((USB->ISTR & USB_ISTR_CTR) == 0){
return;
}
if(needzlp) EP_WriteIRQ(0, (uint8_t*)0, 0);
}
}
}
static inline void get_descriptor(){
switch(setup_packet.wValue){
case DEVICE_DESCRIPTOR:
wr0(USB_DeviceDescriptor, sizeof(USB_DeviceDescriptor));
break;
case CONFIGURATION_DESCRIPTOR:
wr0(USB_ConfigDescriptor, sizeof(USB_ConfigDescriptor));
break;
case STRING_LANG_DESCRIPTOR:
wr0((const uint8_t *)&USB_StringLangDescriptor, STRING_LANG_DESCRIPTOR_SIZE_BYTE);
break;
case STRING_MAN_DESCRIPTOR:
wr0((const uint8_t *)&USB_StringManufacturingDescriptor, USB_StringManufacturingDescriptor.bLength);
break;
case STRING_PROD_DESCRIPTOR:
wr0((const uint8_t *)&USB_StringProdDescriptor, USB_StringProdDescriptor.bLength);
break;
case STRING_SN_DESCRIPTOR:
wr0((const uint8_t *)&USB_StringSerialDescriptor, USB_StringSerialDescriptor.bLength);
break;
case DEVICE_QUALIFIER_DESCRIPTOR:
wr0(USB_DeviceQualifierDescriptor, USB_DeviceQualifierDescriptor[0]);
break;
default:
break;
}
}
static uint8_t configuration = 0; // reply for GET_CONFIGURATION (==1 if configured)
static inline void std_d2h_req(){
uint16_t status = 0; // bus powered
switch(setup_packet.bRequest){
case GET_DESCRIPTOR:
get_descriptor();
break;
case GET_STATUS:
EP_WriteIRQ(0, (uint8_t *)&status, 2); // send status: Bus Powered
break;
case GET_CONFIGURATION:
EP_WriteIRQ(0, &configuration, 1);
break;
default:
break;
}
}
static inline void std_h2d_req(){
switch(setup_packet.bRequest){
case SET_ADDRESS:
// new address will be assigned later - after acknowlegement or request to host
USB_Dev.USB_Addr = setup_packet.wValue;
break;
case SET_CONFIGURATION:
// Now device configured
USB_Dev.USB_Status = USB_STATE_CONFIGURED;
configuration = setup_packet.wValue;
break;
default:
break;
}
}
/*
bmRequestType: 76543210
7 direction: 0 - host->device, 1 - device->host
65 type: 0 - standard, 1 - class, 2 - vendor
4..0 getter: 0 - device, 1 - interface, 2 - endpoint, 3 - other
*/
/**
* Endpoint0 (control) handler
*/
void EP0_Handler(){
uint16_t epstatus = USB->EPnR[0]; // EP0R on input -> return this value after modifications
uint8_t reqtype = setup_packet.bmRequestType & 0x7f;
uint8_t dev2host = (setup_packet.bmRequestType & 0x80) ? 1 : 0;
int rxflag = RX_FLAG(epstatus);
if(rxflag && SETUP_FLAG(epstatus)){
switch(reqtype){
case STANDARD_DEVICE_REQUEST_TYPE: // standard device request
if(dev2host){
std_d2h_req();
}else{
std_h2d_req();
EP_WriteIRQ(0, (uint8_t *)0, 0);
}
break;
case STANDARD_ENDPOINT_REQUEST_TYPE: // standard endpoint request
if(setup_packet.bRequest == CLEAR_FEATURE){
EP_WriteIRQ(0, (uint8_t *)0, 0);
}
break;
case VENDOR_REQUEST_TYPE:
vendor_handler(&setup_packet);
break;
case CONTROL_REQUEST_TYPE:
switch(setup_packet.bRequest){
case GET_LINE_CODING:
EP_WriteIRQ(0, (uint8_t*)&lineCoding, sizeof(lineCoding));
break;
case SET_LINE_CODING: // omit this for next stage, when data will come
break;
case SET_CONTROL_LINE_STATE:
usbON = 1;
clstate_handler(setup_packet.wValue);
break;
case SEND_BREAK:
usbON = 0;
break_handler();
break;
default:
break;
}
if(setup_packet.bRequest != GET_LINE_CODING) EP_WriteIRQ(0, (uint8_t *)0, 0); // write acknowledgement
break;
default:
EP_WriteIRQ(0, (uint8_t *)0, 0);
}
}else if(rxflag){ // got data over EP0 or host acknowlegement
if(endpoints[0].rx_cnt){
if(setup_packet.bRequest == SET_LINE_CODING){
linecoding_handler((usb_LineCoding*)ep0databuf);
}
}
} else if(TX_FLAG(epstatus)){ // package transmitted
// now we can change address after enumeration
if ((USB->DADDR & USB_DADDR_ADD) != USB_Dev.USB_Addr){
USB->DADDR = USB_DADDR_EF | USB_Dev.USB_Addr;
// change state to ADRESSED
USB_Dev.USB_Status = USB_STATE_ADDRESSED;
}
}
epstatus = KEEP_DTOG(USB->EPnR[0]);
if(rxflag) epstatus ^= USB_EPnR_STAT_TX; // start ZLP/data transmission
else epstatus &= ~USB_EPnR_STAT_TX; // or leave unchanged
// keep DTOGs, clear CTR_RX,TX, set RX VALID
USB->EPnR[0] = (epstatus & ~(USB_EPnR_CTR_RX|USB_EPnR_CTR_TX)) ^ USB_EPnR_STAT_RX;
}
/**
* Write data to EP buffer (called from IRQ handler)
* @param number - EP number
* @param *buf - array with data
* @param size - its size
*/
void EP_WriteIRQ(uint8_t number, const uint8_t *buf, uint16_t size){
if(size > endpoints[number].txbufsz) size = endpoints[number].txbufsz;
uint16_t N2 = (size + 1) >> 1;
// the buffer is 16-bit, so we should copy data as it would be uint16_t
uint16_t *buf16 = (uint16_t *)buf;
uint32_t *out = (uint32_t *)endpoints[number].tx_buf;
for(int i = 0; i < N2; ++i, ++out){
*out = buf16[i];
}
USB_BTABLE->EP[number].USB_COUNT_TX = size;
}
/**
* Write data to EP buffer (called outside IRQ handler)
* @param number - EP number
* @param *buf - array with data
* @param size - its size
*/
void EP_Write(uint8_t number, const uint8_t *buf, uint16_t size){
EP_WriteIRQ(number, buf, size);
uint16_t status = KEEP_DTOG(USB->EPnR[number]);
// keep DTOGs, clear CTR_TX & set TX VALID to start transmission
USB->EPnR[number] = (status & ~(USB_EPnR_CTR_TX)) ^ USB_EPnR_STAT_TX;
}
/*
* Copy data from EP buffer into user buffer area
* @param *buf - user array for data
* @return amount of data read
*/
int EP_Read(uint8_t number, uint16_t *buf){
int sz = endpoints[number].rx_cnt;
if(!sz) return 0;
endpoints[number].rx_cnt = 0;
int n = (sz + 1) >> 1;
uint32_t *in = (uint32_t *)endpoints[number].rx_buf;
if(n){
for(int i = 0; i < n; ++i, ++in)
buf[i] = *(uint16_t*)in;
}
return sz;
}

View File

@ -0,0 +1,186 @@
/*
* This file is part of the MLX90640 project.
* Copyright 2022 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __USB_LIB_H__
#define __USB_LIB_H__
#include <wchar.h>
#include "usbhw.h"
#define EP0DATABUF_SIZE (64)
#define LASTADDR_DEFAULT (STM32ENDPOINTS * 8)
// bmRequestType & 0x7f
#define STANDARD_DEVICE_REQUEST_TYPE 0
#define STANDARD_ENDPOINT_REQUEST_TYPE 2
#define VENDOR_REQUEST_TYPE 0x40
#define CONTROL_REQUEST_TYPE 0x21
// bRequest, standard; for bmRequestType == 0x80
#define GET_STATUS 0x00
#define GET_DESCRIPTOR 0x06
#define GET_CONFIGURATION 0x08
// for bmRequestType == 0
#define CLEAR_FEATURE 0x01
#define SET_FEATURE 0x03 // unused
#define SET_ADDRESS 0x05
#define SET_DESCRIPTOR 0x07 // unused
#define SET_CONFIGURATION 0x09
// for bmRequestType == 0x81, 1 or 0xB2
#define GET_INTERFACE 0x0A // unused
#define SET_INTERFACE 0x0B // unused
#define SYNC_FRAME 0x0C // unused
#define VENDOR_REQUEST 0x01 // unused
// Class-Specific Control Requests
#define SEND_ENCAPSULATED_COMMAND 0x00 // unused
#define GET_ENCAPSULATED_RESPONSE 0x01 // unused
#define SET_COMM_FEATURE 0x02 // unused
#define GET_COMM_FEATURE 0x03 // unused
#define CLEAR_COMM_FEATURE 0x04 // unused
#define SET_LINE_CODING 0x20
#define GET_LINE_CODING 0x21
#define SET_CONTROL_LINE_STATE 0x22
#define SEND_BREAK 0x23
// control line states
#define CONTROL_DTR 0x01
#define CONTROL_RTS 0x02
// wValue
#define DEVICE_DESCRIPTOR 0x100
#define CONFIGURATION_DESCRIPTOR 0x200
#define STRING_LANG_DESCRIPTOR 0x300
#define STRING_MAN_DESCRIPTOR 0x301
#define STRING_PROD_DESCRIPTOR 0x302
#define STRING_SN_DESCRIPTOR 0x303
#define DEVICE_QUALIFIER_DESCRIPTOR 0x600
#define RX_FLAG(epstat) (epstat & USB_EPnR_CTR_RX)
#define TX_FLAG(epstat) (epstat & USB_EPnR_CTR_TX)
#define SETUP_FLAG(epstat) (epstat & USB_EPnR_SETUP)
// EPnR bits manipulation
#define KEEP_DTOG_STAT(EPnR) (EPnR & ~(USB_EPnR_STAT_RX|USB_EPnR_STAT_TX|USB_EPnR_DTOG_RX|USB_EPnR_DTOG_TX))
#define KEEP_DTOG(EPnR) (EPnR & ~(USB_EPnR_DTOG_RX|USB_EPnR_DTOG_TX))
// USB state: uninitialized, addressed, ready for use
typedef enum{
USB_STATE_DEFAULT,
USB_STATE_ADDRESSED,
USB_STATE_CONFIGURED,
USB_STATE_CONNECTED
} USB_state;
// EP types
#define EP_TYPE_BULK 0x00
#define EP_TYPE_CONTROL 0x01
#define EP_TYPE_ISO 0x02
#define EP_TYPE_INTERRUPT 0x03
#define LANG_US (uint16_t)0x0409
#define _USB_STRING_(name, str) \
static const struct name \
{ \
uint8_t bLength; \
uint8_t bDescriptorType; \
uint16_t bString[(sizeof(str) - 2) / 2]; \
\
} \
name = {sizeof(name), 0x03, str}
#define _USB_LANG_ID_(name, lng_id) \
\
static const struct name \
{ \
uint8_t bLength; \
uint8_t bDescriptorType; \
uint16_t bString; \
\
} \
name = {0x04, 0x03, lng_id}
#define STRING_LANG_DESCRIPTOR_SIZE_BYTE (4)
// EP0 configuration packet
typedef struct {
uint8_t bmRequestType;
uint8_t bRequest;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
} config_pack_t;
// endpoints state
typedef struct{
uint16_t *tx_buf; // transmission buffer address
uint16_t txbufsz; // transmission buffer size
uint16_t *rx_buf; // reception buffer address
void (*func)(); // endpoint action function
unsigned rx_cnt : 10; // received data counter
} ep_t;
// USB status & its address
typedef struct {
uint8_t USB_Status;
uint16_t USB_Addr;
}usb_dev_t;
typedef struct {
uint32_t dwDTERate;
uint8_t bCharFormat;
#define USB_CDC_1_STOP_BITS 0
#define USB_CDC_1_5_STOP_BITS 1
#define USB_CDC_2_STOP_BITS 2
uint8_t bParityType;
#define USB_CDC_NO_PARITY 0
#define USB_CDC_ODD_PARITY 1
#define USB_CDC_EVEN_PARITY 2
#define USB_CDC_MARK_PARITY 3
#define USB_CDC_SPACE_PARITY 4
uint8_t bDataBits;
} __attribute__ ((packed)) usb_LineCoding;
typedef struct {
uint8_t bmRequestType;
uint8_t bNotificationType;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
} __attribute__ ((packed)) usb_cdc_notification;
extern ep_t endpoints[];
extern usb_dev_t USB_Dev;
extern uint8_t usbON;
extern config_pack_t setup_packet;
extern uint8_t ep0databuf[];
extern uint8_t ep0dbuflen;
void EP0_Handler();
void EP_WriteIRQ(uint8_t number, const uint8_t *buf, uint16_t size);
void EP_Write(uint8_t number, const uint8_t *buf, uint16_t size);
int EP_Read(uint8_t number, uint16_t *buf);
usb_LineCoding getLineCoding();
void linecoding_handler(usb_LineCoding *lc);
void clstate_handler(uint16_t val);
void break_handler();
void vendor_handler(config_pack_t *packet);
#endif // __USB_LIB_H__

View File

@ -0,0 +1,129 @@
/*
* This file is part of the MLX90640 project.
* Copyright 2022 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "hardware.h"
#include "usb.h"
#include "usbhw.h"
#include "usb_lib.h"
void USB_setup(){
NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn);
NVIC_DisableIRQ(USB_HP_CAN1_TX_IRQn);
USBPU_OFF();
RCC->APB1ENR |= RCC_APB1ENR_USBEN;
RCC->APB2ENR |= USB_RCC;
USB->CNTR = USB_CNTR_FRES; // Force USB Reset
for(uint32_t ctr = 0; ctr < 72000; ++ctr) nop(); // wait >1ms
//uint32_t ctr = 0;
USB->CNTR = 0;
USB->BTABLE = 0;
USB->DADDR = 0;
USB->ISTR = 0;
USB->CNTR = USB_CNTR_RESETM | USB_CNTR_WKUPM; // allow only wakeup & reset interrupts
NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);
USBPU_ON();
}
static uint16_t lastaddr = LASTADDR_DEFAULT;
/**
* Endpoint initialisation
* @param number - EP num (0...7)
* @param type - EP type (EP_TYPE_BULK, EP_TYPE_CONTROL, EP_TYPE_ISO, EP_TYPE_INTERRUPT)
* @param txsz - transmission buffer size @ USB/CAN buffer
* @param rxsz - reception buffer size @ USB/CAN buffer
* @param uint16_t (*func)(ep_t *ep) - EP handler function
* @return 0 if all OK
*/
int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, void (*func)(ep_t ep)){
if(number >= STM32ENDPOINTS) return 4; // out of configured amount
if(txsz > USB_BTABLE_SIZE || rxsz > USB_BTABLE_SIZE) return 1; // buffer too large
if(lastaddr + txsz + rxsz >= USB_BTABLE_SIZE) return 2; // out of btable
USB->EPnR[number] = (type << 9) | (number & USB_EPnR_EA);
USB->EPnR[number] ^= USB_EPnR_STAT_RX | USB_EPnR_STAT_TX_1;
if(rxsz & 1 || rxsz > 512) return 3; // wrong rx buffer size
uint16_t countrx = 0;
if(rxsz < 64) countrx = rxsz / 2;
else{
if(rxsz & 0x1f) return 3; // should be multiple of 32
countrx = 31 + rxsz / 32;
}
USB_BTABLE->EP[number].USB_ADDR_TX = lastaddr;
endpoints[number].tx_buf = (uint16_t *)(USB_BTABLE_BASE + lastaddr*2);
endpoints[number].txbufsz = txsz;
lastaddr += txsz;
USB_BTABLE->EP[number].USB_COUNT_TX = 0;
USB_BTABLE->EP[number].USB_ADDR_RX = lastaddr;
endpoints[number].rx_buf = (uint16_t *)(USB_BTABLE_BASE + lastaddr*2);
lastaddr += rxsz;
USB_BTABLE->EP[number].USB_COUNT_RX = countrx << 10;
endpoints[number].func = func;
return 0;
}
// standard IRQ handler
void usb_lp_can_rx0_isr(){
if(USB->ISTR & USB_ISTR_RESET){
usbON = 0;
// Reinit registers
USB->CNTR = USB_CNTR_RESETM | USB_CNTR_CTRM | USB_CNTR_SUSPM | USB_CNTR_WKUPM;
USB->ISTR = 0;
// Endpoint 0 - CONTROL
// ON USB LS size of EP0 may be 8 bytes, but on FS it should be 64 bytes!
lastaddr = LASTADDR_DEFAULT;
// clear address, leave only enable bit
USB->DADDR = USB_DADDR_EF;
USB_Dev.USB_Status = USB_STATE_DEFAULT;
USB->ISTR = ~USB_ISTR_RESET;
if(EP_Init(0, EP_TYPE_CONTROL, USB_EP0_BUFSZ, USB_EP0_BUFSZ, EP0_Handler)){
return;
}
}
if(USB->ISTR & USB_ISTR_CTR){
// EP number
uint8_t n = USB->ISTR & USB_ISTR_EPID;
// copy status register
uint16_t epstatus = USB->EPnR[n];
// copy received bytes amount
endpoints[n].rx_cnt = USB_BTABLE->EP[n].USB_COUNT_RX & 0x3FF; // low 10 bits is counter
// check direction
if(USB->ISTR & USB_ISTR_DIR){ // OUT interrupt - receive data, CTR_RX==1 (if CTR_TX == 1 - two pending transactions: receive following by transmit)
if(n == 0){ // control endpoint
if(epstatus & USB_EPnR_SETUP){ // setup packet -> copy data to conf_pack
EP_Read(0, (uint16_t*)&setup_packet);
ep0dbuflen = 0;
// interrupt handler will be called later
}else if(epstatus & USB_EPnR_CTR_RX){ // data packet -> push received data to ep0databuf
ep0dbuflen = endpoints[0].rx_cnt;
EP_Read(0, (uint16_t*)&ep0databuf);
}
}
}
// call EP handler
if(endpoints[n].func) endpoints[n].func(endpoints[n]);
}
if(USB->ISTR & USB_ISTR_SUSP){ // suspend -> still no connection, may sleep
usbON = 0;
USB->CNTR |= USB_CNTR_FSUSP | USB_CNTR_LP_MODE;
USB->ISTR = ~USB_ISTR_SUSP;
}
if(USB->ISTR & USB_ISTR_WKUP){ // wakeup
USB->CNTR &= ~(USB_CNTR_FSUSP | USB_CNTR_LP_MODE); // clear suspend flags
USB->ISTR = ~USB_ISTR_WKUP;
}
}

View File

@ -0,0 +1,105 @@
/*
* This file is part of the MLX90640 project.
* Copyright 2022 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef USBHW_H__
#define USBHW_H__
#include <stm32f1.h>
#define USB_RCC RCC_APB2ENR_IOPAEN
// max endpoints number
#define STM32ENDPOINTS 8
/**
* Buffers size definition
**/
#define USB_BTABLE_SIZE 512
// first 64 bytes of USB_BTABLE are registers!
//#define USB_EP0_BASEADDR 64
// for USB FS EP0 buffers are from 8 to 64 bytes long (64 for PL2303)
#define USB_EP0_BUFSZ 64
// USB transmit buffer size (64 for PL2303)
#define USB_TXBUFSZ 64
// USB receive buffer size (64 for PL2303)
#define USB_RXBUFSZ 64
// EP1 - interrupt - buffer size
#define USB_EP1BUFSZ 8
#define USB_BTABLE_BASE 0x40006000
#define USB_BASE ((uint32_t)0x40005C00)
#define USB ((USB_TypeDef *) USB_BASE)
#ifdef USB_BTABLE
#undef USB_BTABLE
#endif
#define USB_BTABLE ((USB_BtableDef *)(USB_BTABLE_BASE))
#define USB_ISTR_EPID 0x0000000F
#define USB_FNR_LSOF_0 0x00000800
#define USB_FNR_lSOF_1 0x00001000
#define USB_LPMCSR_BESL_0 0x00000010
#define USB_LPMCSR_BESL_1 0x00000020
#define USB_LPMCSR_BESL_2 0x00000040
#define USB_LPMCSR_BESL_3 0x00000080
#define USB_EPnR_CTR_RX 0x00008000
#define USB_EPnR_DTOG_RX 0x00004000
#define USB_EPnR_STAT_RX 0x00003000
#define USB_EPnR_STAT_RX_0 0x00001000
#define USB_EPnR_STAT_RX_1 0x00002000
#define USB_EPnR_SETUP 0x00000800
#define USB_EPnR_EP_TYPE 0x00000600
#define USB_EPnR_EP_TYPE_0 0x00000200
#define USB_EPnR_EP_TYPE_1 0x00000400
#define USB_EPnR_EP_KIND 0x00000100
#define USB_EPnR_CTR_TX 0x00000080
#define USB_EPnR_DTOG_TX 0x00000040
#define USB_EPnR_STAT_TX 0x00000030
#define USB_EPnR_STAT_TX_0 0x00000010
#define USB_EPnR_STAT_TX_1 0x00000020
#define USB_EPnR_EA 0x0000000F
#define USB_COUNTn_RX_BLSIZE 0x00008000
#define USB_COUNTn_NUM_BLOCK 0x00007C00
#define USB_COUNTn_RX 0x0000003F
#define USB_TypeDef USB_TypeDef_custom
typedef struct {
__IO uint32_t EPnR[STM32ENDPOINTS];
__IO uint32_t RESERVED[STM32ENDPOINTS];
__IO uint32_t CNTR;
__IO uint32_t ISTR;
__IO uint32_t FNR;
__IO uint32_t DADDR;
__IO uint32_t BTABLE;
} USB_TypeDef;
typedef struct{
__IO uint32_t USB_ADDR_TX;
__IO uint32_t USB_COUNT_TX;
__IO uint32_t USB_ADDR_RX;
__IO uint32_t USB_COUNT_RX;
} USB_EPDATA_TypeDef;
typedef struct{
__IO USB_EPDATA_TypeDef EP[STM32ENDPOINTS];
} USB_BtableDef;
void USB_setup();
int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, void (*func)());
#endif // USBHW_H__

View File

@ -0,0 +1,2 @@
#define BUILD_NUMBER "50"
#define BUILD_DATE "2022-09-10"