Unistable shutter

This commit is contained in:
Edward Emelianov 2024-10-07 14:29:27 +03:00
parent 1633c668e9
commit 6b87f1e1f2
46 changed files with 11205 additions and 11567 deletions

View File

@ -0,0 +1,10 @@
BINARY := shutter
# MCU code
MCU ?= F103x6
# change this linking script depending on particular MCU model,
LDSCRIPT ?= stm32f103x6.ld
DEFINES := -DUSB1_16 -DSTM32F10X_LD
INC_DIR := ../../inc
include ../../makefile.f1
include ../../../makefile.stm32

View File

@ -0,0 +1,135 @@
Shutter control
===============
Works with bi-stable shutter.
You can find the device in `/dev/shutterX` (symlink to `/dev/ttyUSBX`).
After powered on, the device will try to close shutter (during one second).
The CCD (or manual) controlled input allows to open/close shutter by changing its level (depending on `ccdactive` parameter).
If current configuration can't open or close the shutter immediately, it will be opened/closed as soon as possible, while the input holds its level.
When the shutter is closed, the device will send over USB a message like
```
exptime=10000
shutter=closed
```
with calculated value of opened time and current shutter state.
If the error occured and shutter can't be closed, user will read `exp=cantclose`, shutter will come into error state. You should close the shutter by external command in that case.
After the shutter completely opened, user can read a message `shutter=opened`.
## Pinout
**PB0** (pullup in) - hall (or reed switch) sensor input (active low) - opened shutter detector
**PB11** (pullup in) - CCD or button input: open at low signal, close at high
**PA3** (ADC in) - shutter voltage (approx 1/12 U)
**PA5** (PP out) - TLE5205 IN2
**PA6** (PP out) - TLE5205 IN1
**PA7** (pullup in) - TLE5205 FB
**PA10** (PP out) - USB pullup (active low)
**PA11**, **PA12** - USB
**PA13**, **PA14** - SWD
## Commands
### debugging options:
* '0' - shutter OPE
* '1' - shutter CLO
* '2' - shutter OFF
* '3' - shutter HIZ
* 'W' - test watchdog
### configuration:
* '< n' - voltage on discharged capacitor (*100)
* '> n' - voltage on fully charged capacitor (*100)
* '# n' - duration of electrical pulse to open/close shutter (ms)
* '$ n' - duration of mechanical work to completely open/close shutter (ms)
* '* n' - shutter voltage multiplier
* '/ n' - shutter voltage divider (V=Vadc*M/D)
* 'c n' - open shutter when CCD ext level is n (0/1)
* 'd' - dump current config
* 'e' - erase flash storage
* 'h n' - shutter is opened when hall level is n (0/1)
* 's' - save configuration into flash
### common control:
* 'A' - get raw ADC values
* 'C' - close shutter / abort exposition
* 'E n' - expose for n milliseconds
* 'O' - open shutter
* 'R' - software reset
* 'S' - get shutter state; also hall and ccd inputs state (1 - active)
* 't' - get MCU temperature (/10degC)
* 'T' - get Tms
* 'v' - get Vdd (/100V)
* 'V' - get shutter voltage (/100V)
Any wrong long message will echo back. Any wrong short command will show help list.
## Shutter control
Commands '0' ... '3' should be used only for debugging purposes.
To open/close shutter use only 'O', 'C' and 'E' commands.
When opening or closing shutter you will first receive an answer: `OK` if command could be done or `ERR` if there's insufficient voltage on capacitor or shutter is absent.
After opened the message `shutter=opened` will appear. After closing you will receive messages `exptime=xxx` (when `xxx` is approx. exp. time in milliseconds) and `shutter=closed`.
Command 'E' could return `OK`, `ERR` or `ERRNUM`/`I32OVERFLOW` in wrong number format (number could be decimal, 0x.. - hexadecimal, b.. - binary or 0.. - octal).
"Status" command in this case will return `shutter=exposing` and `expfor=xxx` - the given exposition time.
When exposition starts you will receive message `OK` and `shutter=opened`. After its end you'll got `exptime=...`, `shutter=closed`.
If shutter can't be closed, you will give a lots of `exp=cantclose` and different error messages until problem be solved. To stop this error messages give command 'O'.
## Different commands
* 'A' will show raw values for all ADC channels: 0. - capacitor voltage, 1 - MCU temperature, 2 - MCU Vdd. You will give messages like `adcX=val`.
* 't' - `mcut=val`, where val = T*10 degrC.
* 'T' - `tms=val`, val in ms.
* 'v' - `vdd=val`, val in V*100
* 'V' - `voltage=val`, val in V*100
* 'S' - several answers:
* `shutter=`: `closed`, `opened`, `error`, `process`, `wait` or `exposing` - shutter state
* `expfor=...` (only when exposing by command Exxx) - show given exposition time
* `exptime=...` (only when shutter is opened) - show time since opening
* `regstate=`: `open`, `close`, `off` or `hiZ` - TLE5205 outputs state
* `fbstate=`: `0` or `1` - TLE5205 FB out state (1 - error: insufficient voltage or shutter absent)
* `hall=`: `0` or `1` - 1 for opened shutter, 0 for closed
* `ccd=`: `0` or `1` - 1 for active (closed contacts) state of "CCD" input
## Configuration
All configuration stored in MCU flash memory, to dump current config just enter command 'd' and you will give an answer like
```
userconf_sz=16
ccdactive=1
hallactive=0
minvoltage=400
workvoltage=700
shuttertime=20
waitingtime=30
shtrvmul=143
shtrvdiv=25
```
* `userconf_sz` - "magick" number to determine first non-empty record in flash storage. The aligned size of one record in bytes.
* `ccdactive` - is level of 'CCD' input to open shutter (1 to open on high and 0 to open on low signal), change this value with command `c`.
* `hallactive` - the same for Hall sensor or reed switch mounted on shutter to indicate opened state, change with `h`.
* `minvoltage` - voltage level on discharged capacitor (V*100 Volts), when shutter is in opened/closed state, the power will be off from coils reaching this level. Can be from 1V to 10V. Change with `<`.
* `workvoltage` - minimal level (V*100 Volts) of charged capacitor to start process of opening/closing, you can open/close shutter only with higher voltage. Can be from 5V to 100V. Change with `>`.
* `shuttertime` - maximal time (milliseconds) of holding active voltage on shutter's coils. Can be from 5ms to 1s. Change with `#`.
* `waitingtime` - waiting time for the shutter to finish its job. The expose time can't be less than this value. Can be from 5ms to 1s. Change with `$`.
* `shtrvmul` - multiplier to calculate capacitor voltage from voltage on ADC input of MCU. Vcap = V*shtrvmul/shtrvdiv. Can be from 1 to 65535. Change with `*`.
* `shtrvdiv` - divider of capacitor voltage calculation. Can be from 1 to 65535. Change with `/`.
All changes of configuration processed in RAM and acts immediately after changed. If you want to store changes, use command `s`.

View File

@ -0,0 +1,122 @@
/*
* This file is part of the shutter project.
* Copyright 2023 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 "adc.h"
#ifdef EBUG
#include "usb.h"
#include "proto.h"
#endif
uint16_t ADC_array[NUMBER_OF_ADC_CHANNELS*9];
void adc_setup(){
uint32_t ctr = 0;
// Enable clocking
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
__DSB();
// DMA configuration
DMA1_Channel1->CPAR = (uint32_t) (&(ADC1->DR));
DMA1_Channel1->CMAR = (uint32_t)(ADC_array);
DMA1_Channel1->CNDTR = NUMBER_OF_ADC_CHANNELS * 9;
DMA1_Channel1->CCR = DMA_CCR_MINC | DMA_CCR_MSIZE_0 | DMA_CCR_PSIZE_0
| DMA_CCR_CIRC | DMA_CCR_PL | DMA_CCR_EN;
RCC->CFGR = (RCC->CFGR & ~(RCC_CFGR_ADCPRE)) | RCC_CFGR_ADCPRE_DIV8; // ADC clock = RCC / 8
// sampling time - 239.5 cycles for channels 3, 16 and 17
ADC1->SMPR2 = ADC_SMPR2_SMP3;
ADC1->SMPR1 = ADC_SMPR1_SMP16 | ADC_SMPR1_SMP17;
// sequence order: 3 -> 16 -> 17
ADC1->SQR3 = (3 << 0) | (16<<5) | (17 << 10);
ADC1->SQR1 = (NUMBER_OF_ADC_CHANNELS - 1) << 20; // amount of conversions
ADC1->CR1 = ADC_CR1_SCAN; // scan mode
// DMA, continuous mode; enable vref & Tsens; enable SWSTART as trigger
ADC1->CR2 = ADC_CR2_DMA | ADC_CR2_TSVREFE | ADC_CR2_CONT | ADC_CR2_EXTSEL | ADC_CR2_EXTTRIG;
// wake up ADC
ADC1->CR2 |= ADC_CR2_ADON;
__DSB();
// wait for Tstab - at least 1us
while(++ctr < 0xff) nop();
// calibration
ADC1->CR2 |= ADC_CR2_RSTCAL;
ctr = 0; while((ADC1->CR2 & ADC_CR2_RSTCAL) && ++ctr < 0xfffff);
ADC1->CR2 |= ADC_CR2_CAL;
ctr = 0; while((ADC1->CR2 & ADC_CR2_CAL) && ++ctr < 0xfffff);
// clear possible errors and start
ADC1->SR = 0;
ADC1->CR2 |= ADC_CR2_SWSTART;
}
/**
* @brief getADCval - calculate median value for `nch` channel
* @param nch - number of channel
* @return
*/
uint16_t getADCval(int nch){
int i, addr = nch;
register uint16_t temp;
#define PIX_SORT(a,b) { if ((a)>(b)) PIX_SWAP((a),(b)); }
#define PIX_SWAP(a,b) { temp=(a);(a)=(b);(b)=temp; }
uint16_t p[9];
for(i = 0; i < 9; ++i, addr += NUMBER_OF_ADC_CHANNELS){ // first we should prepare array for optmed
p[i] = ADC_array[addr];
/*
#ifdef EBUG
USB_sendstr("ADC"); USB_putbyte('0'+i);
USB_putbyte('='); USB_sendstr(u2str(p[i]));
newline();
#endif
*/
}
PIX_SORT(p[1], p[2]) ; PIX_SORT(p[4], p[5]) ; PIX_SORT(p[7], p[8]) ;
PIX_SORT(p[0], p[1]) ; PIX_SORT(p[3], p[4]) ; PIX_SORT(p[6], p[7]) ;
PIX_SORT(p[1], p[2]) ; PIX_SORT(p[4], p[5]) ; PIX_SORT(p[7], p[8]) ;
PIX_SORT(p[0], p[3]) ; PIX_SORT(p[5], p[8]) ; PIX_SORT(p[4], p[7]) ;
PIX_SORT(p[3], p[6]) ; PIX_SORT(p[1], p[4]) ; PIX_SORT(p[2], p[5]) ;
PIX_SORT(p[4], p[7]) ; PIX_SORT(p[4], p[2]) ; PIX_SORT(p[6], p[4]) ;
PIX_SORT(p[4], p[2]) ;
return p[4];
#undef PIX_SORT
#undef PIX_SWAP
}
// get voltage @input nch (1/100V)
uint32_t getADCvoltage(int nch){
uint32_t v = getADCval(nch);
v *= getVdd();
v /= 0xfff; // 12bit ADC
return v;
}
// return MCU temperature (degrees of celsius * 10)
int32_t getMCUtemp(){
// Temp = (V25 - Vsense)/Avg_Slope + 25
// V_25 = 1.45V, Slope = 4.3e-3
int32_t Vsense = getVdd() * getADCval(CHTSENS);
int32_t temperature = 593920 - Vsense; // 593920 == 145*4096
temperature /= 172; // == /(4096*10*4.3e-3), 10 - to convert from *100 to *10
temperature += 250;
return(temperature);
}
// return Vdd * 100 (V)
uint32_t getVdd(){
uint32_t vdd = 120 * 4096; // 1.2V
vdd /= getADCval(CHVREF);
return vdd;
}

View File

@ -0,0 +1,42 @@
/*
* This file is part of the shutter project.
* Copyright 2023 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>
#define NUMBER_OF_ADC_CHANNELS (3)
// shutter voltage channel
#define CHSHTR (0)
// channels for Tsens and Vref
#define CHTSENS (1)
#define CHVREF (2)
/**
* @brief ADC_array - array for ADC channels with median filtering:
* 0 - U shutter
* 1 - internal Tsens
* 2 - Vref
*/
extern uint16_t ADC_array[];
void adc_setup();
int32_t getMCUtemp();
uint32_t getVdd();
uint16_t getADCval(int nch);
uint32_t getADCvoltage(int nch);

View File

@ -0,0 +1,192 @@
/*
* This file is part of the shutter project.
* Copyright 2023 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 "flash.h"
#include "proto.h"
#include "usb.h" // printout
#include <string.h> // memcpy
extern const uint32_t __varsstart, _BLOCKSIZE;
static const uint32_t FLASH_blocksize = (uint32_t)&_BLOCKSIZE;
static uint32_t maxCnum = 1024 / sizeof(user_conf); // can't use blocksize here
#define USERCONF_INITIALIZER { \
.userconf_sz = sizeof(user_conf) \
,.hallactive = 0 \
,.ccdactive = 0 \
,.workvoltage = 400 \
,.shuttertime = 50 \
,.shtrVmul = 1449 \
,.shtrVdiv = 113 \
,.holdingPWM = 25 \
,.chkhall = 0 \
}
static int write2flash(const void*, const void*, uint32_t);
const user_conf *Flash_Data = (const user_conf *)(&__varsstart);
user_conf the_conf = USERCONF_INITIALIZER;
static int currentconfidx = -1; // index of current configuration
/**
* @brief binarySearch - binary search in flash for last non-empty cell
* any struct searched should have its sizeof() @ the first field!!!
* @param l - left index
* @param r - right index (should be @1 less than last index!)
* @param start - starting address
* @param stor_size - size of structure to search
* @return index of non-empty cell or -1
*/
static int binarySearch(int r, const uint8_t *start, int stor_size){
int l = 0;
while(r >= l){
int mid = l + (r - l) / 2;
const uint8_t *s = start + mid * stor_size;
if(*((const uint16_t*)s) == stor_size){
if(*((const uint16_t*)(s + stor_size)) == 0xffff){ // next is free
return mid;
}else{ // element is to the right
l = mid + 1;
}
}else{ // element is to the left
r = mid - 1;
}
}
return -1; // not found
}
/**
* @brief flashstorage_init - initialization of user conf storage
* run in once @ start
*/
void flashstorage_init(){
if(FLASH_SIZE > 0 && FLASH_SIZE < 20000){
uint32_t flsz = FLASH_SIZE * 1024; // size in bytes
flsz -= (uint32_t)(&__varsstart) - FLASH_BASE;
maxCnum = flsz / sizeof(user_conf);
}
// -1 if there's no data at all & flash is clear; maxnum-1 if flash is full
currentconfidx = binarySearch((int)maxCnum-2, (const uint8_t*)Flash_Data, sizeof(user_conf));
if(currentconfidx > -1){
memcpy(&the_conf, &Flash_Data[currentconfidx], sizeof(user_conf));
}
}
// store new configuration
// @return 0 if all OK
int store_userconf(){
// maxnum - 3 means that there always should be at least one empty record after last data
// for binarySearch() checking that there's nothing more after it!
if(currentconfidx > (int)maxCnum - 3){ // there's no more place
currentconfidx = 0;
if(erase_storage(-1)) return 1;
}else ++currentconfidx; // take next data position (0 - within first run after firmware flashing)
return write2flash((const void*)&Flash_Data[currentconfidx], &the_conf, sizeof(the_conf));
}
static int write2flash(const void *start, const void *wrdata, uint32_t stor_size){
int ret = 0;
if (FLASH->CR & FLASH_CR_LOCK){ // unloch flash
FLASH->KEYR = FLASH_KEY1;
FLASH->KEYR = FLASH_KEY2;
}
while (FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH;
FLASH->SR = FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPRTERR; // clear all flags
FLASH->CR |= FLASH_CR_PG;
const uint16_t *data = (const uint16_t*) wrdata;
volatile uint16_t *address = (volatile uint16_t*) start;
uint32_t i, count = (stor_size + 1) / 2;
for(i = 0; i < count; ++i){
*(volatile uint16_t*)(address + i) = data[i];
while(FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH;
if(*(volatile uint16_t*)(address + i) != data[i]){
USB_sendstr("Error: flash is corrupted\n");
ret = 1;
break;
}
if(FLASH->SR & FLASH_SR_PGERR){
USB_sendstr("Error: FLASH_SR_PGERR\n");
ret = 1; // program error - meet not 0xffff
break;
}
FLASH->SR = FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPRTERR;
}
FLASH->CR &= ~(FLASH_CR_PG);
return ret;
}
// erase Nth page of flash storage (flash should be prepared!)
static int erase_pageN(int N){
int ret = 0;
FLASH->AR = (uint32_t)Flash_Data + N*FLASH_blocksize;
FLASH->CR |= FLASH_CR_STRT;
while(FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH;
FLASH->SR = FLASH_SR_EOP;
if(FLASH->SR & FLASH_SR_WRPRTERR){ /* Check Write protection error */
ret = 1;
FLASH->SR = FLASH_SR_WRPRTERR; /* Clear the flag by software by writing it at 1*/
}
return ret;
}
// erase full storage (npage < 0) or its nth page; @return 0 if all OK
int erase_storage(int npage){
int ret = 0;
uint32_t end = 1, start = 0, flsz = 0;
if(FLASH_SIZE > 0 && FLASH_SIZE < 20000){
flsz = FLASH_SIZE * 1024; // size in bytes
flsz -= (uint32_t)Flash_Data - FLASH_BASE;
}
end = flsz / FLASH_blocksize;
if(end == 0 || end >= FLASH_SIZE) return 1;
if(npage > -1){ // erase only one page
if((uint32_t)npage >= end) return 1;
start = npage;
end = start + 1;
}
if((FLASH->CR & FLASH_CR_LOCK) != 0){
FLASH->KEYR = FLASH_KEY1;
FLASH->KEYR = FLASH_KEY2;
}
while(FLASH->SR & FLASH_SR_BSY) IWDG->KR = IWDG_REFRESH;
FLASH->SR = FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPRTERR;
FLASH->CR |= FLASH_CR_PER;
for(uint32_t i = start; i < end; ++i){
if(erase_pageN(i)){
ret = 1;
break;
}
}
FLASH->CR &= ~FLASH_CR_PER;
return ret;
}
void dump_userconf(){
USB_sendstr("userconf_sz="); USB_sendstr(u2str(the_conf.userconf_sz));
USB_sendstr("\nccdactive="); USB_putbyte('0' + the_conf.ccdactive);
USB_sendstr("\nhallactive="); USB_putbyte('0' + the_conf.hallactive);
USB_sendstr("\nchkhall="); USB_putbyte('0' + the_conf.chkhall);
USB_sendstr("\nholdpwm="); USB_sendstr(u2str(the_conf.holdingPWM));
USB_sendstr("\nworkvoltage="); USB_sendstr(u2str(the_conf.workvoltage));
USB_sendstr("\nshuttertime="); USB_sendstr(u2str(the_conf.shuttertime));
USB_sendstr("\nshtrvmul="); USB_sendstr(u2str(the_conf.shtrVmul));
USB_sendstr("\nshtrvdiv="); USB_sendstr(u2str(the_conf.shtrVdiv));
newline();
}

View File

@ -0,0 +1,46 @@
/*
* This file is part of the shutter project.
* Copyright 2023 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>
#define FLASH_SIZE_REG ((uint32_t)0x1FFFF7E0)
#define FLASH_SIZE *((uint16_t*)FLASH_SIZE_REG)
/*
* struct to save user configurations
*/
typedef struct __attribute__((packed, aligned(4))){
uint16_t userconf_sz; // "magick number"
uint16_t workvoltage; // minimal working voltage (*100)
uint16_t shuttertime; // opening time (ms) - maximal time to hold 100% PWM
uint16_t shtrVmul; // multiplier of shutter voltage calculation
uint16_t shtrVdiv; // divider -//-
uint8_t holdingPWM; // holding PWM level (percents)
uint8_t ccdactive : 1; // ccd active (open shutter at): 0 - low, 1 - high
uint8_t hallactive : 1; // hall sensor active (shutter is opened when): 0 - low, 1 - high
uint8_t chkhall : 1; // check (1) or not (0) hall sensor to get shutter state
} user_conf;
extern user_conf the_conf;
void flashstorage_init();
int store_userconf();
int erase_storage(int npage);
void dump_userconf();

View File

@ -0,0 +1,113 @@
/*
* This file is part of the shutter project.
* Copyright 2023 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 "adc.h"
#include "flash.h"
#include "hardware.h"
TRUE_INLINE void iwdg_setup(){
uint32_t tmout = 16000000;
/* Enable the peripheral clock RTC */
/* (1) Enable the LSI (40kHz) */
/* (2) Wait while it is not ready */
RCC->CSR |= RCC_CSR_LSION; /* (1) */
while((RCC->CSR & RCC_CSR_LSIRDY) != RCC_CSR_LSIRDY){if(--tmout == 0) break;} /* (2) */
/* Configure IWDG */
/* (1) Activate IWDG (not needed if done in option bytes) */
/* (2) Enable write access to IWDG registers */
/* (3) Set prescaler by 64 (1.6ms for each tick) */
/* (4) Set reload value to have a rollover each 2s */
/* (5) Check if flags are reset */
/* (6) Refresh counter */
IWDG->KR = IWDG_START; /* (1) */
IWDG->KR = IWDG_WRITE_ACCESS; /* (2) */
IWDG->PR = IWDG_PR_PR_1; /* (3) */
IWDG->RLR = 1250; /* (4) */
tmout = 16000000;
while(IWDG->SR){if(--tmout == 0) break;} /* (5) */
IWDG->KR = IWDG_REFRESH; /* (6) */
}
/*
Pinout:
PB10 (pullup in) - CCD or button input (depends on settings)
PB11 (alternate: TIM2_CH4) - PWM output for shutter feeding
PB12 (pullup in) - hall (or reed switch) sensor input (opened/closed shutter detector)
PB13 (PP out) - USB pullup (active low)
PA3 (ADC in) - shutter voltage (approx 1/12 U)
PA9 (reserved) - USART1 Tx
PA10 (reserved) - USART1 Rx
PA11,12 - USB
PA13,14 - SWD
*/
TRUE_INLINE void gpio_setup(){
// Enable clocks to the GPIO subsystems, turn on AFIO clocking to disable SWD/JTAG
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_AFIOEN;
__DSB();
// turn off SWJ/JTAG
// AFIO->MAPR = AFIO_MAPR_SWJ_CFG_DISABLE;
// AFIO->MAPR = AFIO_MAPR_SWJ_CFG_JTAGDISABLE; // for PA15
GPIOA->CRL = CRL(3, CNF_ANALOG | MODE_INPUT);
GPIOB->CRH = CRH(10, CNF_PUDINPUT | MODE_INPUT) | CRH(12, CNF_PUDINPUT | MODE_INPUT) |
CRH(13, CNF_PPOUTPUT | MODE_NORMAL);
// if ccdactive==0 - shutter is closed when no output signals
//GPIOB->ODR = ((the_conf.hallactive) ? 0: 1) | ((the_conf.ccdactive) ? 0 : 1<<11);
GPIOB->ODR = (1<<10) | (1<<11); // PB10 & PB12 pullup
}
// TIM2_CH4 (36MHz) as PWM @ 65kHz
TRUE_INLINE void pwm_setup(){
// remap TIM2_CH4 to PB11
AFIO->MAPR = (AFIO->MAPR & ~AFIO_MAPR_TIM2_REMAP) |
AFIO_MAPR_TIM2_REMAP_PARTIALREMAP2;
GPIOB->CRH |= CRH(11, CNF_AFPP | MODE_NORMAL);
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
// 36 MHz / 100 / 65kHz = 5 ==> 7.2MHz with PWMfreq = 72kHz
TIM2->PSC = 4;
TIM2->ARR = 99;
TIM2->CCR4 = 0; // turned off at start
// mode1: active->inactive; CCR4 preload enable
TIM2->CCMR2 = TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4PE;
// enable PWM output
TIM2->CCER = TIM_CCER_CC4E;
// turn on and generate update event to refresh preloaded buffers
TIM2->CR1 = TIM_CR1_CEN;
TIM2->EGR = TIM_EGR_UG;
}
// set PWM to 0..100%, return 0 if `percent`>100
int set_pwm(uint32_t percent){
if(percent > 100) return 0;
TIM2->CCR4 = percent;
return 1;
}
void hw_setup(){
iwdg_setup();
gpio_setup();
adc_setup();
pwm_setup();
}
uint32_t getShutterVoltage(){
uint32_t val = getADCvoltage(CHSHTR);
val *= the_conf.shtrVmul;
return val / the_conf.shtrVdiv;
}

View File

@ -0,0 +1,36 @@
/*
* This file is part of the shutter 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>
#include "flash.h"
// PB12 - hall, PB10 - CCD (active low)
#define BTNPORT GPIOB
#define HALLPIN (1<<12)
#define CCDPIN (1<<10)
#define CHKHALL() ((HALLPIN == (BTNPORT->IDR & HALLPIN)) == the_conf.hallactive)
#define CHKCCD() ((CCDPIN == (BTNPORT->IDR & CCDPIN)) == the_conf.ccdactive)
extern volatile uint32_t Tms;
void hw_setup();
uint32_t getShutterVoltage();
int set_pwm(uint32_t percent);

Binary file not shown.

View File

@ -1,4 +1,4 @@
14252338628960429
14250610752930601
Battery
BatteryClip_Keystone_54_D16-19mm
Battery clip for batteries with a diameter between 16-19 mm: https://www.keyelco.com/product.cfm/product_id/826
@ -58365,10 +58365,3 @@ test point THT pad
0
1
1
kicad_unistable
qr
0
0
0

View File

@ -1,12 +1,12 @@
%TF.GenerationSoftware,KiCad,Pcbnew,8.0.5*%
%TF.CreationDate,2024-10-02T16:41:57+03:00*%
%TF.CreationDate,2024-10-04T09:10:30+03:00*%
%TF.ProjectId,shutter,73687574-7465-4722-9e6b-696361645f70,rev?*%
%TF.SameCoordinates,Original*%
%TF.FileFunction,Copper,L2,Bot*%
%TF.FilePolarity,Positive*%
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 8.0.5) date 2024-10-02 16:41:57*
G04 Created by KiCad (PCBNEW 8.0.5) date 2024-10-04 09:10:30*
%MOMM*%
%LPD*%
G01*
@ -30,46 +30,46 @@ G04 Aperture macros list*
20,1,$1+$1,$8,$9,$2,$3,0*%
G04 Aperture macros list end*
%TA.AperFunction,ComponentPad*%
%ADD10O,1.905000X2.000000*%
%ADD10R,1.700000X1.700000*%
%TD*%
%TA.AperFunction,ComponentPad*%
%ADD11R,1.905000X2.000000*%
%ADD11R,1.000000X1.000000*%
%TD*%
%TA.AperFunction,ComponentPad*%
%ADD12RoundRect,0.250000X0.625000X-0.350000X0.625000X0.350000X-0.625000X0.350000X-0.625000X-0.350000X0*%
%ADD12O,1.000000X1.000000*%
%TD*%
%TA.AperFunction,ComponentPad*%
%ADD13O,1.750000X1.200000*%
%ADD13R,1.905000X2.000000*%
%TD*%
%TA.AperFunction,ComponentPad*%
%ADD14O,1.700000X1.700000*%
%ADD14O,1.905000X2.000000*%
%TD*%
%TA.AperFunction,ComponentPad*%
%ADD15R,1.700000X1.700000*%
%ADD15RoundRect,0.250000X0.350000X0.625000X-0.350000X0.625000X-0.350000X-0.625000X0.350000X-0.625000X0*%
%TD*%
%TA.AperFunction,ComponentPad*%
%ADD16RoundRect,0.250000X-0.350000X-0.625000X0.350000X-0.625000X0.350000X0.625000X-0.350000X0.625000X0*%
%ADD16O,1.200000X1.750000*%
%TD*%
%TA.AperFunction,ComponentPad*%
%ADD17O,1.200000X1.750000*%
%ADD17RoundRect,0.250000X-0.350000X-0.625000X0.350000X-0.625000X0.350000X0.625000X-0.350000X0.625000X0*%
%TD*%
%TA.AperFunction,ComponentPad*%
%ADD18RoundRect,0.250000X0.350000X0.625000X-0.350000X0.625000X-0.350000X-0.625000X0.350000X-0.625000X0*%
%ADD18R,1.600000X1.600000*%
%TD*%
%TA.AperFunction,ComponentPad*%
%ADD19R,1.600000X1.600000*%
%ADD19C,1.600000*%
%TD*%
%TA.AperFunction,ComponentPad*%
%ADD20C,1.600000*%
%ADD20C,4.000000*%
%TD*%
%TA.AperFunction,ComponentPad*%
%ADD21C,4.000000*%
%ADD21O,1.700000X1.700000*%
%TD*%
%TA.AperFunction,ComponentPad*%
%ADD22R,1.000000X1.000000*%
%ADD22RoundRect,0.250000X0.625000X-0.350000X0.625000X0.350000X-0.625000X0.350000X-0.625000X-0.350000X0*%
%TD*%
%TA.AperFunction,ComponentPad*%
%ADD23O,1.000000X1.000000*%
%ADD23O,1.750000X1.200000*%
%TD*%
%TA.AperFunction,ComponentPad*%
%ADD24R,2.600000X2.600000*%
@ -78,10 +78,10 @@ G04 Aperture macros list end*
%ADD25C,2.600000*%
%TD*%
%TA.AperFunction,ViaPad*%
%ADD26C,1.000000*%
%ADD26C,1.200000*%
%TD*%
%TA.AperFunction,ViaPad*%
%ADD27C,1.200000*%
%ADD27C,1.000000*%
%TD*%
%TA.AperFunction,Conductor*%
%ADD28C,0.300000*%
@ -91,109 +91,19 @@ G04 Aperture macros list end*
%TD*%
G04 APERTURE END LIST*
D10*
%TO.P,Q1,3,S*%
%TO.N,GND*%
X123698000Y-90574000D03*
%TO.P,Q1,2,D*%
%TO.N,/DR*%
X126238000Y-90574000D03*
D11*
%TO.P,Q1,1,G*%
%TO.N,Net-(Q1-G)*%
X128778000Y-90574000D03*
%TD*%
D12*
%TO.P,J10,1,Pin_1*%
%TO.N,/SHTRpow*%
X117602000Y-97282000D03*
D13*
%TO.P,J10,2,Pin_2*%
%TO.N,/DR*%
X117602000Y-95282000D03*
%TD*%
D14*
%TO.P,J9,3,Pin_3*%
%TO.N,GND*%
X147421600Y-96113600D03*
%TO.P,J9,2,Pin_2*%
%TO.N,/Tx*%
X149961600Y-96113600D03*
D15*
%TO.P,J9,1,Pin_1*%
%TO.N,/Rx*%
X152501600Y-96113600D03*
%TD*%
D16*
%TO.P,J5,1,Pin_1*%
%TO.N,GND*%
X158528000Y-75438000D03*
D17*
%TO.P,J5,2,Pin_2*%
%TO.N,Net-(D5-A)*%
X160528000Y-75438000D03*
%TD*%
D18*
%TO.P,J2,1,Pin_1*%
%TO.N,GND*%
X158242000Y-107950000D03*
D17*
%TO.P,J2,2,Pin_2*%
%TO.N,Net-(J2-Pin_2)*%
X156242000Y-107950000D03*
%TO.P,J2,3,Pin_3*%
%TO.N,Net-(J2-Pin_3)*%
X154242000Y-107950000D03*
%TO.P,J2,4,Pin_4*%
%TO.N,/LimSW*%
X152242000Y-107950000D03*
%TD*%
D18*
%TO.P,J1,1,Pin_1*%
%TO.N,GND*%
X143764000Y-107950000D03*
D17*
%TO.P,J1,2,Pin_2*%
%TO.N,Net-(J1-Pin_2)*%
X141764000Y-107950000D03*
%TD*%
D15*
%TO.P,J8,1,Pin_1*%
%TO.N,+3V3*%
X162814000Y-104394000D03*
%TD*%
D19*
%TO.P,J11,1,VBUS*%
%TO.N,/VBUS*%
X165462500Y-87500000D03*
D20*
%TO.P,J11,2,D-*%
%TO.N,Net-(J11-D-)*%
X165462500Y-85000000D03*
%TO.P,J11,3,D+*%
%TO.N,Net-(J11-D+)*%
X167462500Y-85000000D03*
%TO.P,J11,4,GND*%
%TO.N,GND*%
X167462500Y-87500000D03*
D21*
%TO.P,J11,5,Shield*%
X170322500Y-80250000D03*
X170322500Y-92250000D03*
%TD*%
D15*
%TO.P,J7,1,Pin_1*%
%TO.N,GND*%
X170942000Y-104394000D03*
%TD*%
%TO.P,J6,1,Pin_1*%
%TO.N,/5V*%
X153924000Y-72390000D03*
%TD*%
D22*
D11*
%TO.P,J4,1,Pin_1*%
%TO.N,/SWCLK*%
X148515000Y-74000000D03*
D23*
D12*
%TO.P,J4,2,Pin_2*%
%TO.N,/SWDIO*%
X147245000Y-74000000D03*
@ -210,6 +120,87 @@ X143435000Y-74000000D03*
%TO.N,/NRST*%
X142165000Y-74000000D03*
%TD*%
D10*
%TO.P,J7,1,Pin_1*%
%TO.N,GND*%
X170942000Y-104394000D03*
%TD*%
D13*
%TO.P,Q1,1,G*%
%TO.N,Net-(Q1-G)*%
X128778000Y-90574000D03*
D14*
%TO.P,Q1,2,D*%
%TO.N,/DR*%
X126238000Y-90574000D03*
%TO.P,Q1,3,S*%
%TO.N,GND*%
X123698000Y-90574000D03*
%TD*%
D15*
%TO.P,J2,1,Pin_1*%
%TO.N,GND*%
X158242000Y-107950000D03*
D16*
%TO.P,J2,2,Pin_2*%
%TO.N,Net-(J2-Pin_2)*%
X156242000Y-107950000D03*
%TO.P,J2,3,Pin_3*%
%TO.N,Net-(J2-Pin_3)*%
X154242000Y-107950000D03*
%TO.P,J2,4,Pin_4*%
%TO.N,/LimSW*%
X152242000Y-107950000D03*
%TD*%
D17*
%TO.P,J5,1,Pin_1*%
%TO.N,GND*%
X158528000Y-75438000D03*
D16*
%TO.P,J5,2,Pin_2*%
%TO.N,Net-(D5-A)*%
X160528000Y-75438000D03*
%TD*%
D18*
%TO.P,J11,1,VBUS*%
%TO.N,/VBUS*%
X165462500Y-87500000D03*
D19*
%TO.P,J11,2,D-*%
%TO.N,Net-(J11-D-)*%
X165462500Y-85000000D03*
%TO.P,J11,3,D+*%
%TO.N,Net-(J11-D+)*%
X167462500Y-85000000D03*
%TO.P,J11,4,GND*%
%TO.N,GND*%
X167462500Y-87500000D03*
D20*
%TO.P,J11,5,Shield*%
X170322500Y-80250000D03*
X170322500Y-92250000D03*
%TD*%
D10*
%TO.P,J9,1,Pin_1*%
%TO.N,/Rx*%
X152501600Y-96113600D03*
D21*
%TO.P,J9,2,Pin_2*%
%TO.N,/Tx*%
X149961600Y-96113600D03*
%TO.P,J9,3,Pin_3*%
%TO.N,GND*%
X147421600Y-96113600D03*
%TD*%
D22*
%TO.P,J10,1,Pin_1*%
%TO.N,/SHTRpow*%
X117602000Y-97282000D03*
D23*
%TO.P,J10,2,Pin_2*%
%TO.N,/DR*%
X117602000Y-95282000D03*
%TD*%
D24*
%TO.P,J3,1,Pin_1*%
%TO.N,GND*%
@ -219,139 +210,105 @@ D25*
%TO.N,/SHTRpow*%
X122543000Y-107916000D03*
%TD*%
D15*
%TO.P,J1,1,Pin_1*%
%TO.N,GND*%
X143764000Y-107950000D03*
D16*
%TO.P,J1,2,Pin_2*%
%TO.N,Net-(J1-Pin_2)*%
X141764000Y-107950000D03*
%TD*%
D26*
%TO.N,GND*%
X136220200Y-92354400D03*
D27*
X135331200Y-73253600D03*
%TO.N,/Tx*%
X150723600Y-87020400D03*
%TO.N,/PWM*%
X132486400Y-92964000D03*
%TO.N,/USBDP*%
X155092400Y-83000000D03*
%TO.N,GND*%
X160274000Y-105410000D03*
D26*
X142225000Y-79868000D03*
D27*
X147828000Y-99314000D03*
X154686000Y-78486000D03*
X165658800Y-107467400D03*
%TO.N,/LimSW*%
X152247600Y-104292400D03*
X160274000Y-105410000D03*
X160528000Y-90170000D03*
%TO.N,/5V*%
X158496000Y-97028000D03*
%TO.N,GND*%
X160528000Y-90170000D03*
%TO.N,/PWM*%
X146608800Y-93218000D03*
%TO.N,/LimSW*%
X154965400Y-90754200D03*
%TO.N,/5V*%
X163068000Y-92760800D03*
X163068000Y-79400000D03*
%TO.N,/Rx*%
X153771600Y-86004400D03*
%TO.N,/USBDP*%
X155092400Y-87884000D03*
D27*
%TO.N,GND*%
X136220200Y-92354400D03*
X142225000Y-79868000D03*
D26*
X163068000Y-92760800D03*
%TO.N,/SWDIO*%
X153000000Y-81750000D03*
X147250000Y-76000000D03*
%TO.N,/NRST*%
X137947400Y-86918800D03*
X139832500Y-76332500D03*
%TO.N,/LimSW*%
X154965400Y-90754200D03*
X152247600Y-104292400D03*
%TO.N,/USBDP*%
X155092400Y-87884000D03*
X155092400Y-83000000D03*
%TO.N,/Tx*%
X150723600Y-87020400D03*
%TO.N,/Rx*%
X153771600Y-86004400D03*
%TO.N,/PWM*%
X146608800Y-93218000D03*
X132486400Y-92964000D03*
%TD*%
D28*
%TO.N,GND*%
X135319400Y-91453600D02*
X135319400Y-73265400D01*
X135319400Y-73265400D02*
X135331200Y-73253600D01*
X136220200Y-92354400D02*
X138887200Y-92354400D01*
X141281100Y-80117900D02*
X141502700Y-79868000D01*
X138887200Y-92354400D02*
X140504950Y-90736650D01*
X140504950Y-90736650D02*
X140504950Y-80894050D01*
X140504950Y-80894050D02*
X141281100Y-80117900D01*
X141502700Y-79868000D02*
X142225000Y-79868000D01*
%TO.N,/USBDP*%
X155092400Y-83000000D02*
X155092400Y-87884000D01*
%TO.N,/Rx*%
X153771600Y-86004400D02*
X153771600Y-87122000D01*
X153771600Y-87122000D02*
X152501600Y-88392000D01*
X152501600Y-88392000D02*
X152501600Y-96113600D01*
%TO.N,/Tx*%
X150723600Y-87020400D02*
X150723600Y-87934800D01*
X150723600Y-87934800D02*
X149961600Y-88696800D01*
%TO.N,/LimSW*%
X154965400Y-90754200D02*
X155549600Y-91338400D01*
X155549600Y-91338400D02*
X155549600Y-100990400D01*
X155549600Y-100990400D02*
X152247600Y-104292400D01*
%TO.N,/Tx*%
X149961600Y-96113600D02*
X149961600Y-88696800D01*
%TO.N,/PWM*%
X134061200Y-94081600D02*
X145745200Y-94081600D01*
X132486400Y-92964000D02*
X132943600Y-92964000D01*
X145745200Y-94081600D02*
X146608800Y-93218000D01*
X132943600Y-92964000D02*
X134061200Y-94081600D01*
%TO.N,GND*%
X162560000Y-106857800D02*
X165049200Y-106857800D01*
X160274000Y-105410000D02*
X161721800Y-106857800D01*
X161721800Y-106857800D02*
X162560000Y-106857800D01*
X130810000Y-93218000D02*
X130810000Y-91694000D01*
X147828000Y-99314000D02*
X136906000Y-99314000D01*
X136906000Y-99314000D02*
X130810000Y-93218000D01*
X130810000Y-91694000D02*
X131050400Y-91453600D01*
X131050400Y-91453600D02*
X135319400Y-91453600D01*
X135319400Y-91453600D02*
X136220200Y-92354400D01*
X160020000Y-78486000D02*
X154686000Y-78486000D01*
X160528000Y-78994000D02*
X160020000Y-78486000D01*
X140504950Y-80894050D02*
X141281100Y-80117900D01*
X138887200Y-92354400D02*
X140504950Y-90736650D01*
X161721800Y-106857800D02*
X162560000Y-106857800D01*
X141281100Y-80117900D02*
X141502700Y-79868000D01*
X165049200Y-106857800D02*
X165658800Y-107467400D01*
X141502700Y-79868000D02*
X142225000Y-79868000D01*
X160274000Y-105410000D02*
X161721800Y-106857800D01*
X160528000Y-78994000D02*
X160020000Y-78486000D01*
X136906000Y-99314000D02*
X130810000Y-93218000D01*
X160528000Y-90170000D02*
X160528000Y-78994000D01*
X130810000Y-91694000D02*
X131050400Y-91453600D01*
X140504950Y-90736650D02*
X140504950Y-80894050D01*
X135319400Y-91453600D02*
X135319400Y-73265400D01*
X147828000Y-99314000D02*
X136906000Y-99314000D01*
X131050400Y-91453600D02*
X135319400Y-91453600D01*
X130810000Y-93218000D02*
X130810000Y-91694000D01*
X135319400Y-73265400D02*
X135331200Y-73253600D01*
X135319400Y-91453600D02*
X136220200Y-92354400D01*
%TO.N,/5V*%
X163068000Y-79400000D02*
X163068000Y-92760800D01*
X163068000Y-92760800D02*
X163068000Y-94742000D01*
X160782000Y-97028000D02*
X158496000Y-97028000D01*
X163068000Y-94742000D02*
X160782000Y-97028000D01*
X163068000Y-92760800D02*
X163068000Y-94742000D01*
%TO.N,GND*%
X160528000Y-90170000D02*
X160528000Y-78994000D01*
%TO.N,/5V*%
X163068000Y-79400000D02*
X163068000Y-92760800D01*
%TO.N,/SWDIO*%
X147250000Y-76650000D02*
X152350000Y-81750000D01*
@ -366,6 +323,39 @@ X137718800Y-78446200D02*
X139832500Y-76332500D01*
X137718800Y-86690200D02*
X137718800Y-78446200D01*
%TO.N,/LimSW*%
X154965400Y-90754200D02*
X155549600Y-91338400D01*
X155549600Y-91338400D02*
X155549600Y-100990400D01*
X155549600Y-100990400D02*
X152247600Y-104292400D01*
%TO.N,/USBDP*%
X155092400Y-83000000D02*
X155092400Y-87884000D01*
%TO.N,/Tx*%
X150723600Y-87020400D02*
X150723600Y-87934800D01*
X149961600Y-96113600D02*
X149961600Y-88696800D01*
X150723600Y-87934800D02*
X149961600Y-88696800D01*
%TO.N,/Rx*%
X153771600Y-86004400D02*
X153771600Y-87122000D01*
X152501600Y-88392000D02*
X152501600Y-96113600D01*
X153771600Y-87122000D02*
X152501600Y-88392000D01*
%TO.N,/PWM*%
X132943600Y-92964000D02*
X134061200Y-94081600D01*
X134061200Y-94081600D02*
X145745200Y-94081600D01*
X145745200Y-94081600D02*
X146608800Y-93218000D01*
X132486400Y-92964000D02*
X132943600Y-92964000D01*
%TD*%
%TA.AperFunction,Conductor*%
%TO.N,/DR*%
@ -3308,12 +3298,12 @@ G37*
%TD.AperFunction*%
%TD*%
D29*
X173500000Y-113000000D02*
X173500000Y-70500000D01*
X112500000Y-113000000D02*
X173500000Y-113000000D01*
X112500000Y-70500000D02*
X112500000Y-113000000D01*
X173500000Y-70500000D02*
X112500000Y-70500000D01*
X173500000Y-113000000D02*
X173500000Y-70500000D01*
X112500000Y-70500000D02*
X112500000Y-113000000D01*
M02*

View File

@ -1,11 +1,11 @@
%TF.GenerationSoftware,KiCad,Pcbnew,8.0.5*%
%TF.CreationDate,2024-10-02T16:41:57+03:00*%
%TF.CreationDate,2024-10-04T09:10:30+03:00*%
%TF.ProjectId,shutter,73687574-7465-4722-9e6b-696361645f70,rev?*%
%TF.SameCoordinates,Original*%
%TF.FileFunction,Profile,NP*%
%FSLAX46Y46*%
G04 Gerber Fmt 4.6, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 8.0.5) date 2024-10-02 16:41:57*
G04 Created by KiCad (PCBNEW 8.0.5) date 2024-10-04 09:10:30*
%MOMM*%
%LPD*%
G01*
@ -15,12 +15,12 @@ G04 APERTURE LIST*
%TD*%
G04 APERTURE END LIST*
D10*
X173500000Y-113000000D02*
X173500000Y-70500000D01*
X112500000Y-113000000D02*
X173500000Y-113000000D01*
X112500000Y-70500000D02*
X112500000Y-113000000D01*
X173500000Y-70500000D02*
X112500000Y-70500000D01*
X173500000Y-113000000D02*
X173500000Y-70500000D01*
X112500000Y-70500000D02*
X112500000Y-113000000D01*
M02*

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,31 @@
Drill report for shutter.kicad_pcb
Created on 2024-10-04T09:10:23+0300
Copper Layer Stackup:
=============================================================
L1 : F.Cu front
L2 : B.Cu back
Drill file 'shutter.drl' contains
plated through holes:
=============================================================
T1 0.600mm 0.0236" (23 holes)
T2 0.650mm 0.0256" (6 holes)
T3 0.750mm 0.0295" (10 holes)
T4 0.950mm 0.0374" (4 holes)
T5 1.000mm 0.0394" (6 holes)
T6 1.100mm 0.0433" (3 holes)
T7 1.300mm 0.0512" (2 holes)
T8 2.300mm 0.0906" (2 holes)
Total plated holes count 56
Not plated through holes are merged with plated holes
unplated through holes:
=============================================================
T9 3.200mm 0.1260" (4 holes)
T10 3.500mm 0.1378" (1 hole)
Total unplated holes count 5

View File

@ -1,12 +1,12 @@
%TF.GenerationSoftware,KiCad,Pcbnew,8.0.5*%
%TF.CreationDate,2024-10-02T16:36:54+03:00*%
%TF.CreationDate,2024-10-04T09:10:24+03:00*%
%TF.ProjectId,shutter,73687574-7465-4722-9e6b-696361645f70,rev?*%
%TF.SameCoordinates,Original*%
%TF.FileFunction,Drillmap*%
%TF.FilePolarity,Positive*%
%FSLAX45Y45*%
G04 Gerber Fmt 4.5, Leading zero omitted, Abs format (unit mm)*
G04 Created by KiCad (PCBNEW 8.0.5) date 2024-10-02 16:36:54*
G04 Created by KiCad (PCBNEW 8.0.5) date 2024-10-04 09:10:24*
%MOMM*%
%LPD*%
G01*
@ -20,14 +20,14 @@ G04 APERTURE LIST*
%ADD16C,0.350000*%
G04 APERTURE END LIST*
D10*
X17350000Y-11300000D02*
X17350000Y-7050000D01*
X11250000Y-11300000D02*
X17350000Y-11300000D01*
X11250000Y-7050000D02*
X11250000Y-11300000D01*
X17350000Y-7050000D02*
X11250000Y-7050000D01*
X17350000Y-11300000D02*
X17350000Y-7050000D01*
X11250000Y-7050000D02*
X11250000Y-11300000D01*
D11*
D10*
X13218640Y-9266400D02*

View File

@ -5,7 +5,7 @@
"Application": "Pcbnew",
"Version": "8.0.5"
},
"CreationDate": "2024-10-02T16:41:57+03:00"
"CreationDate": "2024-10-04T09:10:30+03:00"
},
"GeneralSpecs": {
"ProjectId": {
@ -27,7 +27,7 @@
"PadToPad": 0.2,
"PadToTrack": 0.2,
"TrackToTrack": 0.2,
"MinLineWidth": 0.3,
"MinLineWidth": 0.2,
"TrackToRegion": 0.5,
"RegionToRegion": 0.5
}

View File

@ -1,7 +1,7 @@
M48
; DRILL file {KiCad 8.0.5} date 2024-10-02T16:36:56+0300
; DRILL file {KiCad 8.0.5} date 2024-10-04T09:10:26+0300
; FORMAT={-:-/ absolute / metric / decimal}
; #@! TF.CreationDate,2024-10-02T16:36:56+03:00
; #@! TF.CreationDate,2024-10-04T09:10:26+03:00
; #@! TF.GenerationSoftware,Kicad,Pcbnew,8.0.5
; #@! TF.FileFunction,MixedPlating,1,2
FMAT,2

View File

@ -1,38 +1,34 @@
"Source:","/home/eddy/Docs/SAO/Zeiss-1000/Small_photometer/shutter/kicad/shutter/shutter.kicad_sch"
"Date:","Вт 12 сен 2023 17:34:36"
"Tool:","Eeschema 7.0.5"
"Generator:","/usr/local/share/kicad/plugins/bom_csv_grouped_by_value.py"
"Component Count:","44"
"Collated Components:"
"Item","Qty","Reference(s)","Value","LibPart","Footprint","Datasheet","DNP"
"1","1","C1","2200u, 40V","ALL-rescue:CP1","Capacitor_THT:CP_Radial_D17.0mm_P7.50mm","",""
"2","1","C2","1u","Device:C","Capacitor_SMD:C_0805_2012Metric_Pad1.18x1.45mm_HandSolder","~",""
"3","5","C3, C5, C6, C7, C8","0.1","Device:C","Capacitor_SMD:C_0603_1608Metric_Pad1.08x0.95mm_HandSolder","~",""
"4","1","C4","47u","Device:C_Polarized","Capacitor_Tantalum_SMD:CP_EIA-6032-28_Kemet-C_Pad2.25x2.35mm_HandSolder","~",""
"5","2","C9, C10","6","Device:C","Capacitor_SMD:C_0603_1608Metric_Pad1.08x0.95mm_HandSolder","~",""
"6","5","D1, D2, D3, D4, D5","1N5819","Device:D_Schottky","Diode_SMD:D_SOD-323_HandSoldering","~",""
"7","1","DA1","TLE5205","my_elements:TLE5205","Package_TO_SOT_THT:TO-220-7_P2.54x3.8mm_StaggerEven_Lead5.85mm_TabDown","",""
"8","4","H1, H2, H3, H4","MountingHole","Mechanical:MountingHole","MountingHole:MountingHole_3.2mm_M3","~",""
"9","1","J1","shutter","Connector:Conn_01x02_Female","TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-2-5.08_1x02_P5.08mm_Horizontal","~",""
"10","1","J2","power","Connector:Conn_01x02_Female","TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-2-5.08_1x02_P5.08mm_Horizontal","~",""
"11","1","J3","SWD","Connector:Conn_01x06_Female","Connector_PinHeader_1.27mm:PinHeader_1x06_P1.27mm_Vertical","~",""
"12","1","J4","5v","Connector_Generic:Conn_01x01","Connector_PinHeader_2.54mm:PinHeader_1x01_P2.54mm_Vertical","~",""
"13","1","J5","GND","Connector_Generic:Conn_01x01","Connector_PinHeader_2.54mm:PinHeader_1x01_P2.54mm_Vertical","~",""
"14","1","J6","3v3","Connector_Generic:Conn_01x01","Connector_PinHeader_2.54mm:PinHeader_1x01_P2.54mm_Vertical","~",""
"15","1","J7","USB_B","Connector:USB_B","Connector_USB:USB_B_Lumberg_2411_02_Horizontal"," ~",""
"16","1","J8","hall","Connector:Conn_01x02_Female","Connector_JST:JST_PH_B2B-PH-K_1x02_P2.00mm_Vertical","~",""
"17","1","J9","ccd","Connector:Conn_01x02_Female","Connector_JST:JST_PH_B2B-PH-K_1x02_P2.00mm_Vertical","~",""
"18","1","Q1","DTA114Y","Transistor_BJT:DTA114Y","Package_TO_SOT_SMD:SOT-323_SC-70_Handsoldering","",""
"19","1","R1","6.9","Device:R","Resistor_SMD:R_0603_1608Metric_Pad0.98x0.95mm_HandSolder","~",""
"20","1","R2","56k","Device:R","Resistor_SMD:R_0603_1608Metric_Pad0.98x0.95mm_HandSolder","~",""
"21","3","R3, R5, R6","4k7","Device:R","Resistor_SMD:R_0603_1608Metric_Pad0.98x0.95mm_HandSolder","~",""
"22","1","R4","300 II","Device:R","Resistor_THT:R_Axial_Power_L20.0mm_W6.4mm_P22.40mm","~",""
"23","1","R7","10k","Device:R","Resistor_SMD:R_0603_1608Metric_Pad0.98x0.95mm_HandSolder","~",""
"24","2","R8, R9","22","Device:R","Resistor_SMD:R_0603_1608Metric_Pad0.98x0.95mm_HandSolder","~",""
"25","1","R10","1k5","Device:R","Resistor_SMD:R_0603_1608Metric_Pad0.98x0.95mm_HandSolder","~",""
"26","1","U1","LM1117-3.3","Regulator_Linear:LM1117-3.3","Package_TO_SOT_SMD:SOT-223-3_TabPin2","http://www.ti.com/lit/ds/symlink/lm1117.pdf",""
"27","1","U2","STM32F103C6Tx","MCU_ST_STM32F1:STM32F103C6Tx","Package_QFP:LQFP-48_7x7mm_P0.5mm","http://www.st.com/st-web-ui/static/active/en/resource/technical/document/datasheet/CD00210843.pdf",""
"28","1","U3","USBLC6-2SC6","Power_Protection:USBLC6-2SC6","Package_TO_SOT_SMD:SOT-23-6_Handsoldering","http://www2.st.com/resource/en/datasheet/CD00050750.pdf",""
"29","1","Y1","NX5032GA-8MHz","Device:Crystal","Crystal:Crystal_SMD_5032-2Pin_5.0x3.2mm","~",""
"Reference","Value","Datasheet","Footprint","Qty","DNP"
"C1","1u","~","Capacitor_SMD:C_0805_2012Metric_Pad1.18x1.45mm_HandSolder","1",""
"C2,C4,C5,C6,C7","0.1","~","Capacitor_SMD:C_0603_1608Metric_Pad1.08x0.95mm_HandSolder","5",""
"C3","47u","~","Capacitor_Tantalum_SMD:CP_EIA-6032-28_Kemet-C_Pad2.25x2.35mm_HandSolder","1",""
"C8,C9","6","~","Capacitor_SMD:C_0603_1608Metric_Pad1.08x0.95mm_HandSolder","2",""
"D1","CESD3V3","~","Diode_SMD:D_0805_2012Metric_Pad1.15x1.40mm_HandSolder","1",""
"D2","SMBJ30A","https://www.st.com/resource/en/datasheet/sm6t.pdf","Diode_SMD:D_SMB-SMC_Universal_Handsoldering","1",""
"D3,D4,D5,D6","SS14","~","-- смешанные значения --","4",""
"H1,H2,H3,H4","MountingHole","~","MountingHole:MountingHole_3.2mm_M3_ISO7380","4",""
"J1","ccd","~","Connector_JST:JST_PH_S2B-PH-K_1x02_P2.00mm_Horizontal","1",""
"J2","hall","~","Connector_JST:JST_PH_S4B-PH-K_1x04_P2.00mm_Horizontal","1",""
"J3","ShtrPower","~","TerminalBlock_Phoenix:TerminalBlock_Phoenix_MKDS-1,5-2-5.08_1x02_P5.08mm_Horizontal","1",""
"J4","SWD","~","Connector_PinHeader_1.27mm:PinHeader_1x06_P1.27mm_Vertical","1",""
"J5","5Vext","~","Connector_JST:JST_PH_S2B-PH-K_1x02_P2.00mm_Horizontal","1",""
"J6","5v","~","Connector_PinHeader_2.54mm:PinHeader_1x01_P2.54mm_Vertical","1",""
"J7","GND","~","Connector_PinHeader_2.54mm:PinHeader_1x01_P2.54mm_Vertical","1",""
"J8","3v3","~","Connector_PinHeader_2.54mm:PinHeader_1x01_P2.54mm_Vertical","1",""
"J9","USART1","~","Connector_PinHeader_2.54mm:PinHeader_1x03_P2.54mm_Vertical","1",""
"J10","shutter","~","Connector_JST:JST_PH_S2B-PH-K_1x02_P2.00mm_Horizontal","1",""
"J11","USB_B","~","Connector_USB:USB_B_Lumberg_2411_02_Horizontal","1",""
"Q1","IRL3303","~","Package_TO_SOT_THT:TO-220-3_Horizontal_TabDown","1",""
"Q2","DTA114Y","","Package_TO_SOT_SMD:SOT-323_SC-70_Handsoldering","1",""
"R1","6.9","~","Resistor_SMD:R_0603_1608Metric_Pad0.98x0.95mm_HandSolder","1",""
"R2,R9","100","~","Resistor_SMD:R_0603_1608Metric_Pad0.98x0.95mm_HandSolder","2",""
"R3,R4","33","~","Resistor_SMD:R_0603_1608Metric_Pad0.98x0.95mm_HandSolder","2",""
"R5","56k","~","Resistor_SMD:R_0603_1608Metric_Pad0.98x0.95mm_HandSolder","1",""
"R6,R8","4k7","~","Resistor_SMD:R_0603_1608Metric_Pad0.98x0.95mm_HandSolder","2",""
"R7","10k","~","Resistor_SMD:R_0603_1608Metric_Pad0.98x0.95mm_HandSolder","1",""
"R10,R11","22","~","Resistor_SMD:R_0603_1608Metric_Pad0.98x0.95mm_HandSolder","2",""
"R12","1k5","~","Resistor_SMD:R_0603_1608Metric_Pad0.98x0.95mm_HandSolder","1",""
"U1","LM1117-3.3","http://www.ti.com/lit/ds/symlink/lm1117.pdf","Package_TO_SOT_SMD:SOT-223-3_TabPin2","1",""
"U2","STM32F103C6Tx","http://www.st.com/st-web-ui/static/active/en/resource/technical/document/datasheet/CD00210843.pdf","Package_QFP:LQFP-48_7x7mm_P0.5mm","1",""
"U3","USBLC6-2SC6","http://www2.st.com/resource/en/datasheet/CD00050750.pdf","Package_TO_SOT_SMD:SOT-23-6_Handsoldering","1",""
"Y1","NX5032GA-8MHz","~","Crystal:Crystal_SMD_5032-2Pin_5.0x3.2mm","1",""

Can't render this file because it has a wrong number of fields in line 7.

File diff suppressed because it is too large Load Diff

View File

@ -65,7 +65,7 @@
40
],
"visible_layers": "fffffff_ffffffff",
"zone_display_mode": 1
"zone_display_mode": 0
},
"git": {
"repo_password": "",

View File

@ -116,7 +116,7 @@
},
"rules": {
"max_error": 0.005,
"min_clearance": 0.2,
"min_clearance": 0.22,
"min_connection": 0.2,
"min_copper_edge_clearance": 0.25,
"min_hole_clearance": 0.25,
@ -493,7 +493,7 @@
"gencad": "",
"idf": "",
"netlist": "",
"plot": "svg/",
"plot": "gerbers/",
"pos_files": "",
"specctra_dsn": "",
"step": "",
@ -504,7 +504,7 @@
},
"schematic": {
"annotate_start_num": 0,
"bom_export_filename": "",
"bom_export_filename": "shutter.csv",
"bom_fmt_presets": [],
"bom_fmt_settings": {
"field_delimiter": ",",

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 318 KiB

After

Width:  |  Height:  |  Size: 470 KiB

View File

@ -0,0 +1,62 @@
/*
* This file is part of the shutter 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 "proto.h"
#include "shutter.h"
#include "usb.h"
#define MAXSTRLEN RBINSZ
// eash ERRPERIOD ms show error message if shutter in error state
#define ERRPERIOD 9999
volatile uint32_t Tms = 0;
void sys_tick_handler(void){
++Tms;
}
int main(void){
char inbuff[MAXSTRLEN+1];
StartHSE();
SysTick_Config(72000);
USBPU_OFF();
flashstorage_init();
hw_setup();
USB_setup();
// close shutter and only after that turn on USB pullup
while(!close_shutter() && Tms < 1000) IWDG->KR = IWDG_REFRESH;
USBPU_ON();
uint32_t Terr = Tms + 2*ERRPERIOD;
while(1){
IWDG->KR = IWDG_REFRESH; // refresh watchdog
if(shutterstate == SHUTTER_ERROR && Tms - Terr > ERRPERIOD){
USB_sendstr("shutter=error\n");
Terr = Tms;
}
int l = USB_receivestr(inbuff, MAXSTRLEN);
if(l < 0) USB_sendstr("usb=error\n");
else if(l){
const char *ans = parse_cmd(inbuff);
if(ans) USB_sendstr(ans);
}
process_shutter();
}
}

View File

@ -0,0 +1,4 @@
set FLASH_SIZE 0x20000
source [find interface/stlink-v2-1.cfg]
source [find target/stm32f1x.cfg]

View File

@ -0,0 +1,349 @@
/*
* This file is part of the shutter 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 "adc.h"
#include "flash.h"
#include "hardware.h"
#include "proto.h"
#include "shutter.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:F103/shutter build#" BUILD_NUMBER " @ " BUILD_DATE "\n"
" common control:\n"
"'A' - get raw ADC values\n"
"'C' - close shutter / abort exposition\n"
"'E n' - expose for n milliseconds\n"
"'O' - open shutter\n"
"'P n' - set shutter voltage PWM to n (0..100)\n"
"'R' - software reset\n"
"'S' - get shutter state\n"
"'t' - get MCU temperature (/10degC)\n"
"'T' - get Tms\n"
"'v' - get Vdd (/100V)\n"
"'V' - get shutter voltage (/100V)\n"
" configuration:\n"
"'> n' - minimal working voltage (*100)\n"
"'# n' - duration of electrical pulse to open/close shutter (ms)\n"
"'* n' - shutter voltage multiplier\n"
"'/ n' - shutter voltage divider (V=Vadc*M/D)\n"
"'c n' - open shutter when CCD ext level is n (0/1)\n"
"'d' - dump current config\n"
"'e' - erase flash storage\n"
"'h n' - shutter is opened when sensor level is n (0/1)\n"
"'k n' - check (1) or not (0) shutter state sensor\n"
"'p n' - set holding PWM level to n (0..100%)\n"
"'s' - save configuration into flash\n"
;
static const char *OK = "OK", *ERR = "ERR", *ERRVAL = "ERRVAL\n";
const char *parse_cmd(const char *buf){
if(buf[1] == '\n' || buf[1] == '\r' || !buf[1]){ // one symbol commands
switch(*buf){
case 'd': // dump config
dump_userconf(); return NULL;
break;
case 'e': // erase flash
if(erase_storage(-1)) USB_sendstr(ERR);
else USB_sendstr(OK);
break;
case 's': // save configuration
if(store_userconf()) USB_sendstr(ERR);
else USB_sendstr(OK);
break;
case 'A':
for(int i = 0; i < NUMBER_OF_ADC_CHANNELS; ++i){
USB_sendstr("\nadc");
USB_putbyte('0' + i);
USB_putbyte('=');
USB_sendstr(u2str(getADCval(i)));
}
break;
case 'C':
if(close_shutter()) USB_sendstr(OK);
else USB_sendstr(ERR);
break;
case 'O':
if(open_shutter()) USB_sendstr(OK);
else USB_sendstr(ERR);
break;
case 'R':
USB_sendstr("Soft reset\n");
USB_sendall();
NVIC_SystemReset();
break;
case 'S':
print_shutter_state();
USB_sendstr("\nccd=");
USB_putbyte('0' + CHKCCD());
break;
break;
case 't':
USB_sendstr("mcut=");
USB_sendstr(u2str(getMCUtemp()));
break;
case 'T':
USB_sendstr("tms=");
USB_sendstr(u2str(Tms));
break;
case 'v':
USB_sendstr("vdd=");
USB_sendstr(u2str(getVdd()));
break;
case 'V':
USB_sendstr("voltage=");
USB_sendstr(u2str(getShutterVoltage()));
break;
default:
return helpmsg;
}
}else{ // long messages: cmd + number
uint32_t Num = 0;
char c = *buf++;
char *nxt = getnum(buf, &Num);
if(buf == nxt){
if(Num) return "I32OVERFLOW\n";
return "ERRNUM\n";
}
switch(c){
case '>': // max voltage
if(Num < 500 || Num > 10000) return ERRVAL;
the_conf.workvoltage = Num;
USB_sendstr("workvoltage="); USB_sendstr(u2str(the_conf.workvoltage));
break;
case '#': // shuttertime
if(Num < 5 || Num > 60000) return ERRVAL;
the_conf.shuttertime = Num;
USB_sendstr("shuttertime="); USB_sendstr(u2str(the_conf.shuttertime));
break;
case '*': // mult
if(Num < 1 || Num > 65535) return ERRVAL; // avoid zeroing and overfull
the_conf.shtrVmul = Num;
USB_sendstr("shtrvmul="); USB_sendstr(u2str(the_conf.shtrVmul));
break;
case '/': // div
if(Num < 1 || Num > 65535) return ERRVAL; // avoid zero dividing and overfull
the_conf.shtrVdiv = Num;
USB_sendstr("shtrvdiv="); USB_sendstr(u2str(the_conf.shtrVdiv));
break;
case 'c': // CCD active @
the_conf.ccdactive = Num;
USB_sendstr("ccdactive="); USB_putbyte('0' + the_conf.ccdactive);
break;
case 'h': // HALL active @
the_conf.hallactive = Num;
USB_sendstr("hallactive="); USB_putbyte('0' + the_conf.hallactive);
break;
case 'k': // check shutter state
the_conf.chkhall = Num;
USB_sendstr("chkhall="); USB_putbyte('0' + the_conf.chkhall);
break;
case 'P':
if(Num > 100) return ERRVAL;
set_pwm(Num);
USB_sendstr(OK);
break;
case 'p': // set holding PWM
if(Num > 100) return ERRVAL;
the_conf.holdingPWM = Num;
USB_sendstr("holdpwm="); USB_sendstr(u2str(the_conf.holdingPWM));
break;
case 'E':
if(shutterstate != SHUTTER_CLOSED){
USB_sendstr(ERR);
break;
}
if(expose_shutter(Num)) USB_sendstr(OK);
else USB_sendstr(ERR);
break;
default:
return "Wrongcmd\n";
}
}
USB_putbyte('\n');
return NULL;
}
// 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 shutter 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,124 @@
/*
* This file is part of the shutter project.
* Copyright 2023 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"
// stored data length
int RB_datalen(ringbuffer *b){
if(b->tail >= b->head) return (b->tail - b->head);
else return (b->length - b->head + b->tail);
}
/**
* @brief RB_hasbyte - check if buffer has given byte stored
* @param b - buffer
* @param byte - byte to find
* @return index if found, -1 if none
*/
int RB_hasbyte(ringbuffer *b, uint8_t byte){
if(b->head == b->tail) return -1; // no data in buffer
int startidx = b->head;
if(b->head > b->tail){ //
for(int found = b->head; found < b->length; ++found)
if(b->data[found] == byte) return found;
startidx = 0;
}
for(int found = startidx; found < b->tail; ++found)
if(b->data[found] == byte) return found;
return -1;
}
// poor memcpy
static void mcpy(uint8_t *targ, const uint8_t *src, int l){
while(l--) *targ++ = *src++;
}
// increment head or tail
TRUE_INLINE void incr(ringbuffer *b, volatile int *what, int n){
*what += n;
if(*what >= b->length) *what -= b->length;
}
/**
* @brief RB_read - read data from ringbuffer
* @param b - buffer
* @param s - array to write data
* @param len - max len of `s`
* @return bytes read
*/
int RB_read(ringbuffer *b, uint8_t *s, int len){
int l = RB_datalen(b);
if(!l) return 0;
if(l > len) l = len;
int _1st = b->length - b->head;
if(_1st > l) _1st = l;
if(_1st > len) _1st = len;
mcpy(s, b->data + b->head, _1st);
if(_1st < len && l > _1st){
mcpy(s+_1st, b->data, l - _1st);
incr(b, &b->head, l);
return l;
}
incr(b, &b->head, _1st);
return _1st;
}
/**
* @brief RB_readto fill array `s` with data until byte `byte` (with it)
* @param b - ringbuffer
* @param byte - check byte
* @param s - buffer to write data
* @param len - length of `s`
* @return amount of bytes written (negative, if len<data in buffer)
*/
int RB_readto(ringbuffer *b, uint8_t byte, uint8_t *s, int len){
int idx = RB_hasbyte(b, byte);
if(idx < 0) return 0;
int partlen = idx + 1 - b->head;
// now calculate length of new data portion
if(idx < b->head) partlen += b->length;
if(partlen > len) return -RB_read(b, s, len);
return RB_read(b, s, partlen);
}
/**
* @brief RB_write - write some data to ringbuffer
* @param b - buffer
* @param str - data
* @param l - length
* @return amount of bytes written
*/
int RB_write(ringbuffer *b, const uint8_t *str, int l){
int r = b->length - 1 - RB_datalen(b); // rest length
if(l > r) l = r;
if(!l) return 0;
int _1st = b->length - b->tail;
if(_1st > l) _1st = l;
mcpy(b->data + b->tail, str, _1st);
if(_1st < l){ // add another piece from start
mcpy(b->data, str+_1st, l-_1st);
}
incr(b, &b->tail, l);
return l;
}
// just delete all information in buffer `b`
void RB_clearbuf(ringbuffer *b){
b->head = 0;
b->tail = 0;
}

View File

@ -0,0 +1,35 @@
/*
* This file is part of the shutter project.
* Copyright 2023 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>
typedef struct{
uint8_t *data; // data buffer
const int length; // its length
int head; // head index
int tail; // tail index
} ringbuffer;
int RB_read(ringbuffer *b, uint8_t *s, int len);
int RB_readto(ringbuffer *b, uint8_t byte, uint8_t *s, int len);
int RB_hasbyte(ringbuffer *b, uint8_t byte);
int RB_write(ringbuffer *b, const uint8_t *str, int l);
int RB_datalen(ringbuffer *b);
void RB_clearbuf(ringbuffer *b);

Binary file not shown.

View File

@ -0,0 +1,217 @@
/*
* This file is part of the shutter project.
* Copyright 2023 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 "adc.h"
#include "flash.h"
#include "hardware.h"
#include "proto.h"
#include "shutter.h"
#include "usb.h"
static const char *states[SHUTTER_STATE_AMOUNT] = {
[SHUTTER_ERROR] = "error",
[SHUTTER_OPENED] = "opened",
[SHUTTER_CLOSED] = "closed",
[SHUTTER_PROCESS] = "process",
[SHUTTER_EXPOSE] = "exposing",
};
// hall states
static const char *opcl[2] = {"closed", "opened"};
shutter_state shutterstate = SHUTTER_CLOSED;
static shutter_state nextstate = SHUTTER_CLOSED;
static uint32_t Tstart = 0, Texp = 0, Topened = 0;
/**
* @brief changestate - open/close shutter and set next state to nxt
* @return TRUE if success
*/
static int changestate(shutter_state state){
int hall = CHKHALL();
// check current state: change only if `state`==`shutterstate` but real position differs
if(state == SHUTTER_CLOSED && shutterstate == state){
DBG("Close -> close");
if(!the_conf.chkhall) return TRUE;
else if(hall == 0) return TRUE;
}
if(state == SHUTTER_OPENED && shutterstate == state){
DBG("Open->open");
if(!the_conf.chkhall) return TRUE;
else if(hall == 1) return TRUE;
}
// wait while exposition ends or close shutter manually
if(state == SHUTTER_EXPOSE && shutterstate == state){
DBG("Exposed!");
return FALSE;
}
if(getShutterVoltage() < the_conf.workvoltage){
DBG("Undervoltage!");
return FALSE; // insufficient voltage
}
if(state == SHUTTER_EXPOSE || state == SHUTTER_OPENED){
DBG("Start opening");
set_pwm(100);
}else if(state == SHUTTER_CLOSED){
DBG("Close");
set_pwm(0);
}else{
shutterstate = state;
return TRUE;
}
shutterstate = SHUTTER_PROCESS;
nextstate = state;
Tstart = Tms;
return TRUE;
}
/**
* @brief open_shutter, close shutter - change shutter state
* @return false if can't work due to error (no shutter) or insufficient voltage
*/
int open_shutter(){
return changestate(SHUTTER_OPENED);
}
int close_shutter(){
return changestate(SHUTTER_CLOSED);
}
int expose_shutter(uint32_t exptime){
if(!changestate(SHUTTER_EXPOSE)) return FALSE;
Texp = exptime;
return TRUE;
}
void process_shutter(){
static uint32_t T = 0;
uint32_t V = getShutterVoltage();
int hall = CHKHALL();
if(V < the_conf.workvoltage){
DBG("Undervoltage -> err");
shutterstate = SHUTTER_ERROR;
return;
}
switch(shutterstate){
case SHUTTER_ERROR: // error state: undervoltage or wrong hall state
changestate(nextstate);
return;
break;
case SHUTTER_PROCESS: // process opening or closing: set holding voltage when opene
if(the_conf.chkhall){ // what if hall is active?
switch(nextstate){
case SHUTTER_OPENED:
case SHUTTER_EXPOSE:
if(hall){ // set holding voltage
set_pwm(the_conf.holdingPWM);
shutterstate = nextstate;
Topened = Tstart = Tms;
print_shutter_state();
return;
}
break;
case SHUTTER_CLOSED:
if(!hall){
shutterstate = nextstate;
print_shutter_state();
return;
}
break;
default: // impossible state
nextstate = SHUTTER_CLOSED;
shutterstate = SHUTTER_ERROR;
print_shutter_state();
return;
}
}
if(Tms - Tstart >= the_conf.shuttertime){
DBG("time");
if(the_conf.chkhall && nextstate != SHUTTER_CLOSED){ // error when open
nextstate = SHUTTER_CLOSED;
shutterstate = SHUTTER_ERROR;
print_shutter_state();
return;
}
if(nextstate == SHUTTER_OPENED || nextstate == SHUTTER_EXPOSE){
DBG("Holding PWM");
set_pwm(the_conf.holdingPWM);
Topened = Tms;
}
shutterstate = nextstate;
Tstart = Tms;
print_shutter_state();
}
break;
case SHUTTER_EXPOSE: // wait for exposition ends to close shutter
// now Tstart is time when shutter was opened; wait until Tms - Tstart >= Texp
if(Tms - Tstart < Texp || T == Tms) break; // once per 1ms
T = Tms;
if(!close_shutter()){
if(Tms - Tstart >= Texp + the_conf.shuttertime){ // try to close not more than `waitingtime` ms
USB_sendstr("exp=cantclose\n");
shutterstate = SHUTTER_ERROR;
nextstate = SHUTTER_CLOSED;
}
}else{ DBG("Exp end -> close"); }
break;
default:
if(the_conf.chkhall){
if(hall){
if(shutterstate == SHUTTER_CLOSED) shutterstate = SHUTTER_ERROR;
}else if(shutterstate == SHUTTER_OPENED){
USB_sendstr("exp=errclosed\n");
shutterstate = SHUTTER_ERROR;
nextstate = SHUTTER_CLOSED;
}
}
break;
}
if(shutterstate == SHUTTER_ERROR) return;
static uint8_t oldbtnstate = 0;
uint8_t s = CHKCCD();
if(oldbtnstate == s) return; // button's state not changed
// check button only when can open/close & shutter operations done
if(s){ // CCD active - open shutter
if(!open_shutter()){ oldbtnstate = 0; return; }
}else{ // close
if(!close_shutter()){ oldbtnstate = 1; return; }
}
oldbtnstate = s;
}
void print_shutter_state(){
USB_sendstr("shutter=");
USB_sendstr(states[shutterstate]);
USB_putbyte('\n');
if(the_conf.chkhall){
USB_sendstr("sensor=");
USB_sendstr(opcl[CHKHALL()]);
USB_putbyte('\n');
}
if(shutterstate == SHUTTER_EXPOSE){
USB_sendstr("expfor=");
USB_sendstr(u2str(Texp));
USB_putbyte('\n');
}
if(Tms - Topened > the_conf.shuttertime){
USB_sendstr("exptime=");
USB_sendstr(u2str(Tms - Topened - the_conf.shuttertime));
USB_putbyte('\n');
}
}

View File

@ -0,0 +1 @@
-std=c17

View File

@ -0,0 +1,9 @@
// Add predefined macros for your project here. For example:
// #define THE_ANSWER 42
#define EBUG
#define USB1_16
#define STM32F1
#define STM32F103x8
#define STM32F10X_LD
#define BUILD_NUMBER "1"
#define BUILD_DATE "d"

View File

@ -0,0 +1 @@
[General]

View File

@ -0,0 +1,181 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 14.0.1, 2024-10-07T14:27:51. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{cf63021e-ef53-49b0-b03b-2f2570cdf3b6}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="qlonglong">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="qlonglong" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">KOI8-R</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="int" key="EditorConfiguration.PreferAfterWhitespaceComments">0</value>
<value type="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">false</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">1</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="bool" key="EditorConfiguration.UseIndenter">false</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">2</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">true</value>
<value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
<value type="bool" key="EditorConfiguration.tintMarginArea">true</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap">
<valuemap type="QVariantMap" key="AutoTest.ActiveFrameworks">
<value type="bool" key="AutoTest.Framework.Boost">true</value>
<value type="bool" key="AutoTest.Framework.CTest">false</value>
<value type="bool" key="AutoTest.Framework.Catch">true</value>
<value type="bool" key="AutoTest.Framework.GTest">true</value>
<value type="bool" key="AutoTest.Framework.QtQuickTest">true</value>
<value type="bool" key="AutoTest.Framework.QtTest">true</value>
</valuemap>
<value type="bool" key="AutoTest.ApplyFilter">false</value>
<valuemap type="QVariantMap" key="AutoTest.CheckStates"/>
<valuelist type="QVariantList" key="AutoTest.PathFilters"/>
<value type="int" key="AutoTest.RunAfterBuild">0</value>
<value type="bool" key="AutoTest.UseGlobal">true</value>
<valuemap type="QVariantMap" key="ClangTools">
<value type="bool" key="ClangTools.AnalyzeOpenFiles">true</value>
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
<value type="int" key="ClangTools.ParallelJobs">4</value>
<value type="bool" key="ClangTools.PreferConfigFile">false</value>
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
</valuemap>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="DeviceType">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{91347f2c-5221-46a7-80b1-0a054ca02f79}</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="qlonglong" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/home/eddy/Docs/SAO/ELECTRONICS/STM32/F1-srcs/shutter</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">all</value>
</valuelist>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Очистка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Очистка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">По умолчанию</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericBuildConfiguration</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="qlonglong" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Развёртывание</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Развёртывание</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="bool" key="PE.EnvironmentAspect.PrintOnRun">false</value>
<value type="QString" key="PerfRecordArgsId">-e cpu-cycles --call-graph dwarf,4096 -F 250</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName"></value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
<value type="bool" key="ProjectExplorer.RunConfiguration.Customized">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
</valuemap>
<value type="qlonglong" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="qlonglong">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">22</value>
</data>
<data>
<variable>Version</variable>
<value type="int">22</value>
</data>
</qtcreator>

View File

@ -0,0 +1,171 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE QtCreatorProject>
<!-- Written by QtCreator 8.0.2, 2023-06-11T19:24:48. -->
<qtcreator>
<data>
<variable>EnvironmentId</variable>
<value type="QByteArray">{7bd84e39-ca37-46d3-be9d-99ebea85bc0d}</value>
</data>
<data>
<variable>ProjectExplorer.Project.ActiveTarget</variable>
<value type="int">0</value>
</data>
<data>
<variable>ProjectExplorer.Project.EditorSettings</variable>
<valuemap type="QVariantMap">
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
<value type="QString" key="language">Cpp</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
</valuemap>
</valuemap>
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
<value type="QString" key="language">QmlJS</value>
<valuemap type="QVariantMap" key="value">
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
</valuemap>
</valuemap>
<value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
<value type="QByteArray" key="EditorConfiguration.Codec">KOI8-R</value>
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
<value type="int" key="EditorConfiguration.IndentSize">4</value>
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
<value type="bool" key="EditorConfiguration.PreferSingleLineComments">false</value>
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
<value type="int" key="EditorConfiguration.TabSize">8</value>
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
<value type="bool" key="EditorConfiguration.UseIndenter">false</value>
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
<value type="bool" key="EditorConfiguration.cleanIndentation">false</value>
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
<value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
<value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.PluginSettings</variable>
<valuemap type="QVariantMap">
<valuemap type="QVariantMap" key="AutoTest.ActiveFrameworks">
<value type="bool" key="AutoTest.Framework.Boost">true</value>
<value type="bool" key="AutoTest.Framework.CTest">false</value>
<value type="bool" key="AutoTest.Framework.Catch">true</value>
<value type="bool" key="AutoTest.Framework.GTest">true</value>
<value type="bool" key="AutoTest.Framework.QtQuickTest">true</value>
<value type="bool" key="AutoTest.Framework.QtTest">true</value>
</valuemap>
<valuemap type="QVariantMap" key="AutoTest.CheckStates"/>
<value type="int" key="AutoTest.RunAfterBuild">0</value>
<value type="bool" key="AutoTest.UseGlobal">true</value>
<valuemap type="QVariantMap" key="ClangTools">
<value type="bool" key="ClangTools.AnalyzeOpenFiles">true</value>
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
<value type="int" key="ClangTools.ParallelJobs">8</value>
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
</valuemap>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.Target.0</variable>
<valuemap type="QVariantMap">
<value type="QString" key="DeviceType">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{65a14f9e-e008-4c1b-89df-4eaa4774b6e3}</value>
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.BuildConfiguration.0">
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">/Big/Data/00__Electronics/STM32/F303-nolib/blink</value>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">all</value>
</valuelist>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Сборка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Build</value>
</valuemap>
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.1">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildStepList.Step.0">
<valuelist type="QVariantList" key="GenericProjectManager.GenericMakeStep.BuildTargets">
<value type="QString">clean</value>
</valuelist>
<value type="bool" key="ProjectExplorer.BuildStep.Enabled">true</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericMakeStep</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">1</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Очистка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Очистка</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Clean</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">2</value>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ClearSystemEnvironment">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.CustomParsers"/>
<value type="bool" key="ProjectExplorer.BuildConfiguration.ParseStandardOutput">false</value>
<valuelist type="QVariantList" key="ProjectExplorer.BuildConfiguration.UserEnvironmentChanges"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Default</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">GenericProjectManager.GenericBuildConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Развёртывание</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Развёртывание</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
</valuemap>
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
<valuelist type="QVariantList" key="CustomOutputParsers"/>
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.CustomExecutableRunConfiguration</value>
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey"></value>
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
</valuemap>
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
</valuemap>
</data>
<data>
<variable>ProjectExplorer.Project.TargetCount</variable>
<value type="int">1</value>
</data>
<data>
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
<value type="int">22</value>
</data>
<data>
<variable>Version</variable>
<value type="int">22</value>
</data>
</qtcreator>

View File

@ -0,0 +1 @@
-std=c++17

View File

@ -0,0 +1,19 @@
adc.c
adc.h
flash.c
flash.h
hardware.c
hardware.h
main.c
proto.c
proto.h
ringbuffer.c
ringbuffer.h
shutter.c
shutter.h
usb.c
usb.h
usb_lib.c
usb_lib.h
usbhw.c
usbhw.h

View File

@ -0,0 +1,38 @@
/*
* This file is part of the shutter project.
* Copyright 2023 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>
typedef enum{
SHUTTER_ERROR, // shutter is absent?
SHUTTER_OPENED, // open and hold PWM on given percent
SHUTTER_CLOSED, // closed: PWM=0
SHUTTER_PROCESS, // wait full opening or closing
SHUTTER_EXPOSE, // start exposition for given time
SHUTTER_STATE_AMOUNT
} shutter_state;
extern shutter_state shutterstate;
void process_shutter();
int open_shutter();
int close_shutter();
int expose_shutter(uint32_t exptime);
void print_shutter_state();

View File

@ -0,0 +1,6 @@
.
../../inc
../../inc/Fx
../../inc/cm
../../inc/ld
../../inc/startup

View File

@ -0,0 +1,126 @@
/*
* This file is part of the shutter project.
* Copyright 2023 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 <string.h>
#include "hardware.h"
#include "usb.h"
#include "usb_lib.h"
static volatile uint8_t usbbuff[USB_TXBUFSZ]; // temporary buffer for sending data
// ring buffers for incoming and outgoing data
static uint8_t obuf[RBOUTSZ], ibuf[RBINSZ];
volatile ringbuffer rbout = {.data = obuf, .length = RBOUTSZ, .head = 0, .tail = 0};
volatile ringbuffer rbin = {.data = ibuf, .length = RBINSZ, .head = 0, .tail = 0};
// transmission is succesfull
volatile uint8_t bufisempty = 1;
volatile uint8_t bufovrfl = 0;
void send_next(){
if(bufisempty) return;
static int lastdsz = 0;
int buflen = RB_read((ringbuffer*)&rbout, (uint8_t*)usbbuff, USB_TXBUFSZ);
if(!buflen){
if(lastdsz == 64) EP_Write(3, NULL, 0); // send ZLP after 64 bits packet when nothing more to send
lastdsz = 0;
bufisempty = 1;
return;
}
EP_Write(3, (uint8_t*)usbbuff, buflen);
lastdsz = buflen;
}
// blocking send full content of ring buffer
int USB_sendall(){
while(!bufisempty){
if(!usbON) return 0;
}
return 1;
}
// put `buf` into queue to send
int USB_send(const uint8_t *buf, int len){
if(!buf || !usbON || !len) return 0;
while(len){
int a = RB_write((ringbuffer*)&rbout, buf, len);
len -= a;
buf += a;
if(bufisempty){
bufisempty = 0;
send_next();
}
}
return 1;
}
int USB_putbyte(uint8_t byte){
if(!usbON) return 0;
while(0 == RB_write((ringbuffer*)&rbout, &byte, 1)){
if(bufisempty){
bufisempty = 0;
send_next();
}
}
return 1;
}
int USB_sendstr(const char *string){
if(!string || !usbON) return 0;
int len = 0;
const char *b = string;
while(*b++) ++len;
if(!len) return 0;
return USB_send((const uint8_t*)string, len);
}
/**
* @brief USB_receive - get binary data from receiving ring-buffer
* @param buf (i) - buffer for received data
* @param len - length of `buf`
* @return amount of received bytes (negative, if overfull happened)
*/
int USB_receive(uint8_t *buf, int len){
int sz = RB_read((ringbuffer*)&rbin, buf, len);
if(bufovrfl){
RB_clearbuf((ringbuffer*)&rbin);
if(!sz) sz = -1;
else sz = -sz;
bufovrfl = 0;
}
return sz;
}
/**
* @brief USB_receivestr - get string up to '\n' and replace '\n' with 0
* @param buf - receiving buffer
* @param len - its length
* @return strlen or negative value indicating overflow (if so, string won't be ends with 0 and buffer should be cleared)
*/
int USB_receivestr(char *buf, int len){
int l = RB_readto((ringbuffer*)&rbin, '\n', (uint8_t*)buf, len);
if(l == 0) return 0;
if(--l < 0 || bufovrfl) RB_clearbuf((ringbuffer*)&rbin);
else buf[l] = 0; // replace '\n' with strend
if(bufovrfl){
if(l > 0) l = -l;
else l = -1;
bufovrfl = 0;
}
return l;
}

View File

@ -0,0 +1,47 @@
/*
* This file is part of the shutter project.
* Copyright 2023 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 "ringbuffer.h"
#include "usbhw.h"
// sizes of ringbuffers for outgoing and incoming data
#define RBOUTSZ (512)
#define RBINSZ (512)
#define newline() USB_putbyte('\n')
#define USND(s) do{USB_sendstr(s); USB_putbyte('\n');}while(0)
#define STR_HELPER(s) #s
#define STR(s) STR_HELPER(s)
#ifdef EBUG
#define DBG(str) do{USB_sendstr(__FILE__ " (L" STR(__LINE__) "): " str); newline();}while(0)
#else
#define DBG(str)
#endif
extern volatile ringbuffer rbout, rbin;
extern volatile uint8_t bufisempty, bufovrfl;
void send_next();
int USB_sendall();
int USB_send(const uint8_t *buf, int len);
int USB_putbyte(uint8_t byte);
int USB_sendstr(const char *string);
int USB_receive(uint8_t *buf, int len);
int USB_receivestr(char *buf, int len);

View File

@ -0,0 +1,438 @@
/*
* This file is part of the shutter project.
* Copyright 2023 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.h"
#include "usb_lib.h"
#include "usbhw.h"
ep_t endpoints[STM32ENDPOINTS];
static uint16_t USB_Addr = 0;
static usb_LineCoding lineCoding = {115200, 0, 0, 8};
uint8_t ep0databuf[EP0DATABUF_SIZE], setupdatabuf[EP0DATABUF_SIZE];
static config_pack_t *setup_packet = (config_pack_t*) setupdatabuf;
usb_LineCoding getLineCoding(){return lineCoding;}
volatile 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
iMANUFACTURER_DESCR, // iManufacturer
iPRODUCT_DESCR, // iProduct
iSERIAL_DESCR, // 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 */
iINTERFACE_DESCR, /* 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_(LD, LANG_US);
_USB_STRING_(SD, u"0.0.1");
_USB_STRING_(MD, u"Prolific Technology Inc.");
_USB_STRING_(PD, u"USB-Serial Controller");
_USB_STRING_(ID, u"shutter");
static void const *StringDescriptor[iDESCR_AMOUNT] = {
[iLANGUAGE_DESCR] = &LD,
[iMANUFACTURER_DESCR] = &MD,
[iPRODUCT_DESCR] = &PD,
[iSERIAL_DESCR] = &SD,
[iINTERFACE_DESCR] = &ID
};
/*
* 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){
uint16_t c;
if(packet->bmRequestType & 0x80){ // read
switch(packet->wValue){
case 0x8484:
c = 2;
break;
case 0x0080:
c = 1;
break;
case 0x8686:
c = 0xaa;
break;
default:
c = 0;
}
EP_WriteIRQ(0, (uint8_t*)&c, 1);
}else{ // write ZLP
c = 0;
EP_WriteIRQ(0, (uint8_t *)&c, 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(){
uint8_t descrtype = setup_packet->wValue >> 8,
descridx = setup_packet->wValue & 0xff;
switch(descrtype){
case DEVICE_DESCRIPTOR:
wr0(USB_DeviceDescriptor, sizeof(USB_DeviceDescriptor));
break;
case CONFIGURATION_DESCRIPTOR:
wr0(USB_ConfigDescriptor, sizeof(USB_ConfigDescriptor));
break;
case STRING_DESCRIPTOR:
if(descridx < iDESCR_AMOUNT) wr0((const uint8_t *)StringDescriptor[descridx], *((uint8_t*)StringDescriptor[descridx]));
else EP_WriteIRQ(0, (uint8_t*)0, 0);
break;
case DEVICE_QUALIFIER_DESCRIPTOR:
wr0(USB_DeviceQualifierDescriptor, USB_DeviceQualifierDescriptor[0]);
break;
default:
break;
}
}
static uint16_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, (uint8_t*)&configuration, 1);
break;
default:
break;
}
}
// 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
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
send_next();
}
static void receive_Handler(){ // EP2OUT
uint8_t buf[USB_RXBUFSZ];
uint16_t epstatus = KEEP_DTOG(USB->EPnR[2]);
uint8_t sz = EP_Read(2, (uint8_t*)buf);
if(sz){
if(RB_write((ringbuffer*)&rbin, buf, sz) != sz) bufovrfl = 1;
}
// keep stat_tx & set ACK rx, clear RX ctr
USB->EPnR[2] = (epstatus & ~USB_EPnR_CTR_RX) ^ USB_EPnR_STAT_RX;
}
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_Addr = setup_packet->wValue;
break;
case SET_CONFIGURATION:
// Now device configured
configuration = setup_packet->wValue;
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
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_Addr){
USB->DADDR = USB_DADDR_EF | USB_Addr;
usbON = 0;
}
}
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;
#if defined USB1_16
// very bad: what if `size` is odd?
uint32_t *out = (uint32_t *)endpoints[number].tx_buf;
for(int i = 0; i < N2; ++i, ++out){
*out = buf16[i];
}
#elif defined USB2_16
// use memcpy instead?
for(int i = 0; i < N2; i++){
endpoints[number].tx_buf[i] = buf16[i];
}
#else
#error "Define USB1_16 or USB2_16"
#endif
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, uint8_t *buf){
int sz = endpoints[number].rx_cnt;
if(!sz) return 0;
endpoints[number].rx_cnt = 0;
#if defined USB1_16
int n = (sz + 1) >> 1;
uint32_t *in = (uint32_t*)endpoints[number].rx_buf;
uint16_t *out = (uint16_t*)buf;
for(int i = 0; i < n; ++i, ++in)
out[i] = *(uint16_t*)in;
#elif defined USB2_16
// use memcpy instead?
for(int i = 0; i < sz; ++i)
buf[i] = endpoints[number].rx_buf[i];
#else
#error "Define USB1_16 or USB2_16"
#endif
return sz;
}

View File

@ -0,0 +1,170 @@
/*
* This file is part of the shutter project.
* Copyright 2023 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 <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
// string descriptors
enum{
iLANGUAGE_DESCR,
iMANUFACTURER_DESCR,
iPRODUCT_DESCR,
iSERIAL_DESCR,
iINTERFACE_DESCR,
iDESCR_AMOUNT
};
// Types of descriptors
#define DEVICE_DESCRIPTOR 0x01
#define CONFIGURATION_DESCRIPTOR 0x02
#define STRING_DESCRIPTOR 0x03
#define DEVICE_QUALIFIER_DESCRIPTOR 0x06
#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))
// 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}
// 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
uint8_t *rx_buf; // reception buffer address
void (*func)(); // endpoint action function
unsigned rx_cnt : 10; // received data counter
} ep_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 volatile uint8_t usbON;
extern uint8_t ep0databuf[], setupdatabuf[];
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, uint8_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);

View File

@ -0,0 +1,121 @@
/*
* This file is part of the shutter 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 "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);
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);
}
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 * ACCESSZ);
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 = (uint8_t *)(USB_BTABLE_BASE + lastaddr * ACCESSZ);
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;
// 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;
if(EP_Init(0, EP_TYPE_CONTROL, USB_EP0_BUFSZ, USB_EP0_BUFSZ, EP0_Handler)){
return;
}
USB->ISTR = ~USB_ISTR_RESET;
}
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, setupdatabuf);
// interrupt handler will be called later
}else if(epstatus & USB_EPnR_CTR_RX){ // data packet -> push received data to ep0databuf
EP_Read(0, 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,116 @@
/*
* This file is part of the shutter 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>
// USB pullup: PB13
#define USB_RCC RCC_APB2ENR_IOPBEN
#define USBPU_port GPIOB
#define USBPU_pin (1<<13)
#define USBPU_ON() pin_clear(USBPU_port, USBPU_pin)
#define USBPU_OFF() pin_set(USBPU_port, USBPU_pin)
// max endpoints number
#define STM32ENDPOINTS 8
/**
* Buffers size definition
**/
#define USB_BTABLE_SIZE 512
// 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{
#if defined USB2_16
__IO uint16_t USB_ADDR_TX;
__IO uint16_t USB_COUNT_TX;
__IO uint16_t USB_ADDR_RX;
__IO uint16_t USB_COUNT_RX;
#define ACCESSZ (1)
#define BUFTYPE uint8_t
#elif defined USB1_16
__IO uint32_t USB_ADDR_TX;
__IO uint32_t USB_COUNT_TX;
__IO uint32_t USB_ADDR_RX;
__IO uint32_t USB_COUNT_RX;
#define ACCESSZ (2)
#define BUFTYPE uint16_t
#else
#error "Define USB1_16 or USB2_16"
#endif
} 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)());

View File

@ -0,0 +1,2 @@
#define BUILD_NUMBER "12"
#define BUILD_DATE "2024-10-07"