add chrono v2

This commit is contained in:
eddyem
2020-01-25 13:41:13 +03:00
parent 0e40ae2bd5
commit 87b59a8d1d
140 changed files with 110908 additions and 170539 deletions

View File

@@ -0,0 +1,177 @@
/*
* GPS.c
*
* Copyright 2015 Edward V. Emelianov <eddy@sao.ru, 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 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include "GPS.h"
#include "hardware.h"
#include "time.h"
#include "usart.h"
#include "str.h"
#include <string.h> // memcpy
#define GPS_endline() do{usart_send(GPS_USART, "\r\n"); transmit_tbuf(GPS_USART); }while(0)
#define GPS_send_string(str) do{usart_send(GPS_USART, str);}while(0)
gps_status GPS_status = GPS_WAIT;
int need2startseq = 1;
static uint8_t hex(uint8_t n){
return ((n < 10) ? (n+'0') : (n+'A'-10));
}
/**
* Check checksum
*/
static int checksum_true(const char *buf){
char *eol;
uint8_t checksum = 0, cs[3];
if(*buf != '$' || !(eol = getchr(buf, '*'))){
return 0;
}
while(++buf != eol)
checksum ^= (uint8_t)*buf;
++buf;
cs[0] = hex(checksum >> 4);
cs[1] = hex(checksum & 0x0f);
if(buf[0] == cs[0] && buf[1] == cs[1])
return 1;
return 0;
}
static void send_chksum(uint8_t chs){
usart_putchar(GPS_USART, hex(chs >> 4));
usart_putchar(1, hex(chs >> 4));
usart_putchar(GPS_USART, hex(chs & 0x0f));
usart_putchar(1, hex(chs & 0x0f));
}
/**
* Calculate checksum & write message to port
* @param buf - command to write (without leading $ and trailing *)
* return 0 if fails
*/
static void write_with_checksum(const char *buf){
char *txt = NULL;
// clear old buffer data
for(int i = 0; i < 10000; ++i){
if(usartrx(GPS_USART)){
usart_getline(GPS_USART, &txt);
DBG("Old data");
GPS_parse_answer(txt);
break;
}
}
DBG("Send:");
uint8_t checksum = 0;
usart_putchar(GPS_USART, '$');
usart_putchar(1, '$');
GPS_send_string(buf);
SEND(buf);
do{
checksum ^= *buf++;
}while(*buf);
usart_putchar(GPS_USART, '*');
usart_putchar(1, '*');
send_chksum(checksum);
newline();
GPS_endline();
}
/*
* MTK fields format:
* $PMTKxxx,yyy,zzz*2E
* P - proprietary, MTK - always this, xxx - packet type, yyy,zzz - packet data
* Packet types:
* 220 - PMTK_SET_POS_FIX, data - position fix interval (msec, > 200)
* 255 - PMTK_SET_SYNC_PPS_NMEA - turn on/off (def - off) PPS, data = 0/1 -> "$PMTK255,1" turn ON
* 285 - PMTK_SET_PPS_CONFIG - set PPS configuration, data fields:
* 1st - 0-disable, 1-after 1st fix, 2-3D only, 3-2D/3D only, 4-always
* 2nd - 2..998 - pulse width
* 314 - PMTK_API_SET_NMEA_OUTPUT - set output messages, N== N fixes per output,
* order of messages: GLL,RMC,VTG,GGA,GSA,GSV,GRS,GST, only RMC per every pos fix:
* $PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
* 386 - PMTK_API_SET_STATIC_NAV_THD speed threshold (m/s) for static navigation
* $PMTK386,1.5
* ;
*/
/**
* Send starting sequences (get only RMC messages)
*/
void GPS_send_start_seq(){
DBG("Send start seq");
// turn ON PPS:
write_with_checksum("PMTK255,1");
// set pulse width to 10ms with working after 1st fix
write_with_checksum("PMTK285,1,10");
// set only RMC:
write_with_checksum("PMTK314,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0");
// set static speed threshold
write_with_checksum("PMTK386,1.5");
need2startseq = 0;
}
/**
* Parse answer from GPS module
*
* Recommended minimum specific GPS/Transit data
* $GPRMC,hhmmss.sss,status,latitude,N,longitude,E,spd,cog,ddmmyy,mv,mvE,mode*cs
* 1 = UTC of position fix
* 2 = Data status (V=valid, A=invalid)
* 3 = Latitude (ddmm.mmmm)
* 4 = N or S
* 5 = Longitude (dddmm.mmmm)
* 6 = E or W
* 7 = Speed over ground in knots
* 8 = Cource over ground in degrees
* 9 = UT date (ddmmyy)
* 10 = Magnetic variation degrees (Easterly var. subtracts from true course)
* 11 = E or W
* 12 = Mode: N(bad), E(approx), A(auto), D(diff)
* 213457.00,A,4340.59415,N,04127.47560,E,2.494,,290615,,,A*7B
*/
void GPS_parse_answer(const char *buf){
char *ptr;
#if defined USART1PROXY
usart_send(1, buf); newline();
#endif
if(buf[1] == 'P') return; // answers to proprietary messages
if(cmpstr(buf+3, "RMC", 3)){ // not RMC message
need2startseq = 1;
return;
}
if(!checksum_true(buf)){
return; // wrong checksum
}
buf += 7; // skip header
if(*buf == ','){ // time unknown
GPS_status = GPS_WAIT;
return;
}
ptr = getchr(buf, ',');
if(!ptr ) return;
*ptr++ = 0;
if(*ptr == 'A'){
GPS_status = GPS_VALID;
set_time(buf);
}else{
GPS_status = GPS_NOT_VALID;
}
}

View File

@@ -0,0 +1,41 @@
/*
* GPS.h
*
* Copyright 2015 Edward V. Emelianov <eddy@sao.ru, 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 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#pragma once
#ifndef __GPS_H__
#define __GPS_H__
#include "stm32f1.h"
extern int need2startseq;
typedef enum{
GPS_WAIT // wait for satellites
,GPS_NOT_VALID // time known, but not valid
,GPS_VALID
} gps_status;
extern gps_status GPS_status;
void GPS_parse_answer(const char *string);
void GPS_send_start_seq();
#endif // __GPS_H__

View File

@@ -0,0 +1,142 @@
BINARY = chrono
BOOTPORT ?= /dev/ttyUSB0
BOOTSPEED ?= 115200
# MCU FAMILY
FAMILY ?= F1
# MCU code
MCU ?= F103x8
# density (stm32f10x.h, lines 70-84)
DENSITY ?= MD
# change this linking script depending on particular MCU model,
LDSCRIPT ?= stm32f103x8.ld
DEFS = -DVERSION=\"0.0.1\"
# debug
DEFS += -DEBUG
# proxy GPS output over USART1
DEFS += -DUSART1PROXY
INDEPENDENT_HEADERS=
FP_FLAGS ?= -msoft-float -mfloat-abi=soft
ASM_FLAGS ?= -mthumb -mcpu=cortex-m3 -mfix-cortex-m3-ldrd
ARCH_FLAGS = $(ASM_FLAGS) $(FP_FLAGS)
###############################################################################
# Executables
#PREFIX ?= arm-none-eabi
# gcc from arm web site
PREFIX ?= /opt/bin/arm-none-eabi
TOOLCHLIB ?= /opt/arm-none-eabi/lib
RM := rm -f
RMDIR := rmdir
CC := $(PREFIX)-gcc
# don't replace ld with gcc: the binary size would be much greater!!
LD := $(PREFIX)-ld
AR := $(PREFIX)-ar
AS := $(PREFIX)-as
SIZE := $(PREFIX)-size
OBJCOPY := $(PREFIX)-objcopy
OBJDUMP := $(PREFIX)-objdump
GDB := $(PREFIX)-gdb
STFLASH := $(shell which st-flash)
STBOOT := $(shell which stm32flash)
DFUUTIL := $(shell which dfu-util)
###############################################################################
# Source files
OBJDIR = mk
SRC := $(wildcard *.c)
OBJS := $(addprefix $(OBJDIR)/, $(SRC:%.c=%.o))
STARTUP = $(OBJDIR)/startup.o
OBJS += $(STARTUP)
# dependencies: we need them to recompile files if their headers-dependencies changed
DEPS := $(OBJS:.o=.d)
INC_DIR ?= ../inc
INCLUDE := -I$(INC_DIR)/Fx -I$(INC_DIR)/cm
LIB_DIR := $(INC_DIR)/ld
###############################################################################
# C flags
CFLAGS += -O2 -g -D__thumb2__=1 -MD
CFLAGS += -Wall -Werror -Wextra -Wshadow
CFLAGS += -fno-common -ffunction-sections -fdata-sections -fno-stack-protector
CFLAGS += $(ARCH_FLAGS)
###############################################################################
# Linker flags
LDFLAGS += -nostartfiles --static -nostdlibs
LDFLAGS += -L$(LIB_DIR) -L$(TOOLCHLIB)
LDFLAGS += -T$(LDSCRIPT)
###############################################################################
# Used libraries
LDLIBS += -lc $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
DEFS += -DSTM32$(FAMILY) -DSTM32$(MCU) -DSTM32F10X_$(DENSITY)
ELF := $(OBJDIR)/$(BINARY).elf
LIST := $(OBJDIR)/$(BINARY).list
BIN := $(BINARY).bin
HEX := $(BINARY).hex
all: bin list size
elf: $(ELF)
bin: $(BIN)
hex: $(HEX)
list: $(LIST)
ifneq ($(MAKECMDGOALS),clean)
-include $(DEPS)
endif
$(OBJDIR):
mkdir $(OBJDIR)
$(STARTUP): $(INC_DIR)/startup/vector.c
$(CC) $(CFLAGS) $(DEFS) $(INCLUDE) -o $@ -c $<
$(OBJDIR)/%.o: %.c
@echo " CC $<"
$(CC) $(CFLAGS) $(DEFS) $(INCLUDE) -o $@ -c $<
$(BIN): $(ELF)
@echo " OBJCOPY $(BIN)"
$(OBJCOPY) -Obinary $(ELF) $(BIN)
$(HEX): $(ELF)
@echo " OBJCOPY $(HEX)"
$(OBJCOPY) -Oihex $(ELF) $(HEX)
$(LIST): $(ELF)
@echo " OBJDUMP $(LIST)"
$(OBJDUMP) -S $(ELF) > $(LIST)
$(ELF): $(OBJDIR) $(OBJS)
@echo " LD $(ELF)"
$(LD) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $(ELF)
size: $(ELF)
$(SIZE) $(ELF)
clean:
@echo " CLEAN"
$(RM) $(OBJS) $(DEPS) $(ELF) $(HEX) $(LIST)
@rmdir $(OBJDIR) 2>/dev/null || true
flash: $(BIN)
@echo " FLASH $(BIN)"
$(STFLASH) write $(BIN) 0x8000000
boot: $(BIN)
@echo " LOAD $(BIN) through bootloader"
$(STBOOT) -b$(BOOTSPEED) $(BOOTPORT) -w $(BIN)
dfuboot: $(BIN)
@echo " LOAD $(BIN) THROUGH DFU"
$(DFUUTIL) -a0 -D $(BIN) -s 0x08000000
.PHONY: clean flash boot

View File

@@ -0,0 +1,18 @@
Chronometer for downhill competitions
=====================================
## Pinout
- PA9(Tx),PA10 (debug mode) - USART1 - debug console
- PA2(Tx), PA3 - USART2 - GPS
- PB10(Tx), PB11 - USART3 - LIDAR
- PA1 - PPS signal from GPS (EXTI)
- PB8, PB9 - onboard LEDs
- PA4 - TRIG2 - 12V trigger (EXTI) -- not implemented yet
- PA13 - TRIG0 - button0 (EXTI)
- PA14 - TRIG1 - button1/laser/etc (EXTI)
- PA15 - USB pullup

View File

@@ -0,0 +1,70 @@
/*
* This file is part of the chronometer project.
* Copyright 2018 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"
/**
* @brief ADC_array - array for ADC channels with median filtering:
* 0 - Rvar
* 1 - internal Tsens
* 2 - Vref
*/
uint16_t ADC_array[NUMBER_OF_ADC_CHANNELS*9];
/**
* @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];
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
}
// 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(1);
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(2);
return vdd;
}

View File

@@ -0,0 +1,29 @@
/*
* This file is part of the chronometer project.
* Copyright 2018 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/>.
*/
#ifndef ADC_H
#define ADC_H
#include "stm32f1.h"
#define NUMBER_OF_ADC_CHANNELS (3)
extern uint16_t ADC_array[];
int32_t getMCUtemp();
uint32_t getVdd();
uint16_t getADCval(int nch);
#endif // ADC_H

Binary file not shown.

View File

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

View File

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

View File

@@ -0,0 +1,240 @@
/*
* geany_encoding=koi8-r
* flash.c
*
* Copyright 2017 Edward V. Emelianov <eddy@sao.ru, 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 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
/**
ATTENTION!!
This things works only if you will add next section:
.myvars :
{
. = ALIGN(1024);
KEEP(*(.myvars))
} > rom
after section .data
*/
#include "stm32f1.h"
#include <string.h> // memcpy
#include "flash.h"
#include "lidar.h"
#ifdef EBUG
#include "usart.h"
#endif
extern uint32_t _edata, _etext, _sdata;
static int maxnum = FLASH_BLOCK_SIZE / sizeof(user_conf);
typedef struct{
const user_conf all_stored;
} flash_storage;
#define USERCONF_INITIALIZER { \
.userconf_sz = sizeof(user_conf) \
,.dist_min = LIDAR_MIN_DIST \
,.dist_max = LIDAR_MAX_DIST \
}
__attribute__((section(".myvars"))) static const flash_storage Flash_Storage = {
.all_stored = USERCONF_INITIALIZER
};
static const user_conf *Flash_Data = &Flash_Storage.all_stored;
user_conf the_conf = USERCONF_INITIALIZER;
static int erase_flash();
static int currentconfidx = -1; // index of current configuration
/**
* @brief binarySearch - binary search in flash for last non-empty cell
* @param l - left index
* @param r - right index (should be @1 less than last index!)
* @return index of non-empty cell or -1
*/
static int binarySearch(int l, int r){
while(r >= l){
int mid = l + (r - l) / 2;
// If the element is present at the middle
// itself
uint16_t sz = Flash_Data[mid].userconf_sz;
if(sz == sizeof(user_conf)){
if(Flash_Data[mid+1].userconf_sz == 0xffff){
#if 0
SEND("Found at "); printu(1, mid); newline();
#endif
return mid;
}else{ // element is to the right
l = mid + 1;
#if 0
SEND("To the right, L="); printu(1, l); newline();
#endif
}
}else{ // element is to the left
r = mid - 1;
#if 0
SEND("To the left, R="); printu(1, r); newline();
#endif
}
}
DBG("Not found!");
return -1; // not found
}
static int get_gooddata(){
static uint8_t firstrun = 1;
if(firstrun){
firstrun = 0;
if(FLASH_SIZE > 0 && FLASH_SIZE < 20000){
uint32_t flsz = FLASH_SIZE * 1024; // size in bytes
flsz -= (uint32_t)Flash_Data - FLASH_BASE;
#if 0
SEND("All size: "); printu(1, flsz); newline();
#endif
uint32_t usz = (sizeof(user_conf) + 1) / 2;
maxnum = flsz / 2 / usz;
#if 0
SEND("Maxnum: "); printu(1, maxnum); newline();
#endif
}
}
return binarySearch(0, maxnum-2); // -1 if there's no data at all & flash is clear; maxnum-1 if flash is full
}
void get_userconf(){
const user_conf *c = Flash_Data;
int idx = get_gooddata();
if(idx < 0) return; // no data stored
currentconfidx = idx;
memcpy(&the_conf, &c[idx], sizeof(user_conf));
}
// store new configuration
// @return 0 if all OK
int store_userconf(){
IWDG->KR = IWDG_REFRESH;
int ret = 0;
const user_conf *c = Flash_Data;
int idx = currentconfidx;
// maxnum - 3 means that there always should be at least one empty record after last data
if(idx < 0 || idx > maxnum - 3){ // data corruption or there's no more place
idx = 0;
DBG("Need to erase flash!");
if(erase_flash()) return 1;
}else ++idx; // take next data position
currentconfidx = idx;
if (FLASH->CR & FLASH_CR_LOCK){ // unloch flash
FLASH->KEYR = FLASH_KEY1;
FLASH->KEYR = FLASH_KEY2;
}
while (FLASH->SR & FLASH_SR_BSY);
if(FLASH->SR & FLASH_SR_WRPRTERR) return 1; // write protection
FLASH->SR = FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPRTERR; // clear all flags
FLASH->CR |= FLASH_CR_PG;
uint16_t *data = (uint16_t*) &the_conf;
uint16_t *address = (uint16_t*) &c[idx];
uint32_t i, count = (sizeof(user_conf) + 1) / 2;
for (i = 0; i < count; ++i){
*(volatile uint16_t*)(address + i) = data[i];
while (FLASH->SR & FLASH_SR_BSY);
if(FLASH->SR & FLASH_SR_PGERR) ret = 1; // program error - meet not 0xffff
else while (!(FLASH->SR & FLASH_SR_EOP));
FLASH->SR = FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPRTERR;
}
FLASH->CR &= ~(FLASH_CR_PG);
return ret;
}
static int erase_flash(){
int ret = 0;
uint32_t nblocks = 1;
if(FLASH_SIZE > 0 && FLASH_SIZE < 20000){
uint32_t flsz = FLASH_SIZE * 1024; // size in bytes
flsz -= (uint32_t)Flash_Data - FLASH_BASE;
nblocks = flsz / FLASH_BLOCK_SIZE;
#if 0
SEND("N blocks:"); printu(1, nblocks); newline();
#endif
}
for(uint32_t i = 0; i < nblocks; ++i){
IWDG->KR = IWDG_REFRESH;
/* (1) Wait till no operation is on going */
/* (2) Clear error & EOP bits */
/* (3) Check that the Flash is unlocked */
/* (4) Perform unlock sequence */
while ((FLASH->SR & FLASH_SR_BSY) != 0){} /* (1) */
FLASH->SR = FLASH_SR_EOP | FLASH_SR_PGERR | FLASH_SR_WRPRTERR; /* (2) */
/* if (FLASH->SR & FLASH_SR_EOP){
FLASH->SR |= FLASH_SR_EOP;
}*/
if ((FLASH->CR & FLASH_CR_LOCK) != 0){ /* (3) */
FLASH->KEYR = FLASH_KEY1; /* (4) */
FLASH->KEYR = FLASH_KEY2;
}
/* (1) Set the PER bit in the FLASH_CR register to enable page erasing */
/* (2) Program the FLASH_AR register to select a page to erase */
/* (3) Set the STRT bit in the FLASH_CR register to start the erasing */
/* (4) Wait until the EOP flag in the FLASH_SR register set */
/* (5) Clear EOP flag by software by writing EOP at 1 */
/* (6) Reset the PER Bit to disable the page erase */
FLASH->CR |= FLASH_CR_PER; /* (1) */
#if 0
SEND("Delete block number "); printu(1, i); newline();
#endif
FLASH->AR = (uint32_t)Flash_Data + i*FLASH_BLOCK_SIZE; /* (2) */
FLASH->CR |= FLASH_CR_STRT; /* (3) */
while(!(FLASH->SR & FLASH_SR_EOP));
FLASH->SR |= FLASH_SR_EOP; /* (5)*/
if(FLASH->SR & FLASH_SR_WRPRTERR){ /* Check Write protection error */
ret = 1;
DBG("Write protection error!");
FLASH->SR |= FLASH_SR_WRPRTERR; /* Clear the flag by software by writing it at 1*/
break;
}
FLASH->CR &= ~FLASH_CR_PER; /* (6) */
}
return ret;
}
#ifdef EBUG
void dump_userconf(){
SEND("userconf_sz="); printu(1, the_conf.userconf_sz); newline();
SEND("dist_min="); printu(1, the_conf.dist_min); newline();
SEND("dist_max="); printu(1, the_conf.dist_max); newline();
}
void addNrecs(int N){
SEND("Try to store userconf for "); printu(1, N); SEND(" times\n");
for(int i = 0; i < N; ++i){
if(store_userconf()){
SEND("Error @ "); printu(1, i); newline();
return;
}
}
SEND("Curr idx: "); printu(1, currentconfidx); newline();
}
#endif

View File

@@ -0,0 +1,48 @@
/*
* flash.h
*
* Copyright 2017 Edward V. Emelianov <eddy@sao.ru, 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 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#pragma once
#ifndef __FLASH_H__
#define __FLASH_H__
#include <stm32f1.h>
#define FLASH_BLOCK_SIZE (1024)
#define FLASH_SIZE_REG ((uint32_t)0x1FFFF7E0)
#define FLASH_SIZE *((uint16_t*)FLASH_SIZE_REG)
typedef struct{
uint16_t userconf_sz; // "magick number"
uint32_t dist_min; // minimal distance for LIDAR
uint32_t dist_max; // maximal -//-
} user_conf;
extern user_conf the_conf;
void get_userconf();
int store_userconf();
#ifdef EBUG
void dump_userconf();
void addNrecs(int N);
#endif
#endif // __FLASH_H__

View File

@@ -0,0 +1,118 @@
/*
* geany_encoding=koi8-r
* hardware.c - hardware-dependent macros & functions
*
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, 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 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#include "adc.h"
#include "hardware.h"
#include "time.h"
#include "usart.h"
static inline void gpio_setup(){
// Enable clocks to the GPIO subsystems (PB for ADC), turn on AFIO clocking to disable SWD/JTAG
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_AFIOEN;
// turn off SWJ/JTAG
AFIO->MAPR = AFIO_MAPR_SWJ_CFG_DISABLE;
// pullups: PA1 - PPS, PA13/PA14 - buttons
GPIOA->ODR = (1<<12)|(1<<13)|(1<<14)|(1<<15);
// Set leds (PB8) as opendrain output
GPIOB->CRH = CRH(8, CNF_ODOUTPUT|MODE_SLOW) | CRH(9, CNF_ODOUTPUT|MODE_SLOW);
// PPS pin (PA1) - input with weak pullup
GPIOA->CRL = CRL(1, CNF_PUDINPUT|MODE_INPUT);
// Set buttons (PA13/14) as inputs with weak pullups, USB pullup (PA15) - opendrain output
GPIOA->CRH = CRH(13, CNF_PUDINPUT|MODE_INPUT) | CRH(14, CNF_PUDINPUT|MODE_INPUT) |
CRH(15, CNF_ODOUTPUT|MODE_SLOW);
// EXTI: all three EXTI are on PA -> AFIO_EXTICRx = 0
// interrupt on pulse front: buttons - 1->0, PPS - 0->1
EXTI->IMR = EXTI_IMR_MR1 | EXTI_IMR_MR13 | EXTI_IMR_MR14; // unmask
EXTI->RTSR = EXTI_RTSR_TR1; // rising trigger
EXTI->FTSR = EXTI_FTSR_TR13 | EXTI_FTSR_TR14; // falling trigger
NVIC_EnableIRQ(EXTI15_10_IRQn);
NVIC_EnableIRQ(EXTI1_IRQn);
}
static inline void adc_setup(){
GPIOB->CRL |= CRL(0, CNF_ANALOG|MODE_INPUT);
uint32_t ctr = 0;
// Enable clocking
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
RCC->CFGR &= ~(RCC_CFGR_ADCPRE);
RCC->CFGR |= RCC_CFGR_ADCPRE_DIV8; // ADC clock = RCC / 8
// sampling time - 239.5 cycles for channels 8, 16 and 17
ADC1->SMPR2 = ADC_SMPR2_SMP8;
ADC1->SMPR1 = ADC_SMPR1_SMP16 | ADC_SMPR1_SMP17;
// we have three conversions in group -> ADC1->SQR1[L] = 2, order: 8->16->17
ADC1->SQR3 = 8 | (16<<5) | (17<<10);
ADC1->SQR1 = ADC_SQR1_L_1;
ADC1->CR1 |= ADC_CR1_SCAN; // scan mode
// DMA configuration
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
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;
// continuous mode & DMA; enable vref & Tsens; wake up ADC
ADC1->CR2 |= ADC_CR2_DMA | ADC_CR2_TSVREFE | ADC_CR2_CONT | ADC_CR2_ADON;
// 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);
// turn ON ADC
ADC1->CR2 |= ADC_CR2_ADON;
}
void hw_setup(){
gpio_setup();
//adc_setup();
}
void exti1_isr(){ // PPS - PA1
systick_correction();
DBG("exti1");
EXTI->PR = EXTI_PR_PR1;
}
void exti15_10_isr(){ // PA13 - button0, PA14 - button1
if(EXTI->PR & EXTI_PR_PR13){
/*
if(trigger_ms[0] == DIDNT_TRIGGERED){ // prevent bounce
trigger_ms[0] = Timer;
memcpy(&trigger_time[0], &current_time, sizeof(curtime));
}
*/
DBG("exti13");
EXTI->PR = EXTI_PR_PR13;
}
if(EXTI->PR & EXTI_PR_PR14){
/*
if(trigger_ms[3] == DIDNT_TRIGGERED){ // prevent bounce
trigger_ms[3] = Timer;
memcpy(&trigger_time[3], &current_time, sizeof(curtime));
}
*/
DBG("exti14");
EXTI->PR = EXTI_PR_PR14;
}
}

View File

@@ -0,0 +1,63 @@
/*
* geany_encoding=koi8-r
* hardware.h
*
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, 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 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#pragma once
#ifndef __HARDWARE_H__
#define __HARDWARE_H__
#include "stm32f1.h"
// onboard LEDs - PB8/PB9
#define LED0_port GPIOB
#define LED0_pin (1<<8)
#define LED1_port GPIOB
#define LED1_pin (1<<9)
// PPS pin - PA1
#define PPS_port GPIOA
#define PPS_pin (1<<1)
// Buttons' state: PA13 (0)/PA14 (1)
#define GET_BTN0() ((GPIOA->IDR & (1<<13)) ? 0 : 1)
#define GET_BTN1() ((GPIOA->IDR & (1<<14)) ? 0 : 1)
#define GET_PPS() ((GPIOA->IDR & (1<<1)) ? 1 : 0)
// USB pullup - PA15
#define USBPU_port GPIOA
#define USBPU_pin (1<<15)
#define USBPU_ON() pin_clear(USBPU_port, USBPU_pin)
#define USBPU_OFF() pin_set(USBPU_port, USBPU_pin)
#define LED_blink() pin_toggle(LED0_port, LED0_pin)
#define LED_on() pin_clear(LED0_port, LED0_pin)
#define LED_off() pin_set(LED0_port, LED0_pin)
#define LED1_blink() pin_toggle(LED1_port, LED1_pin)
#define LED1_on() pin_clear(LED1_port, LED1_pin)
#define LED1_off() pin_set(LED1_port, LED1_pin)
// GPS USART == USART2, LIDAR USART == USART3
#define GPS_USART (2)
#define LIDAR_USART (3)
void hw_setup();
#endif // __HARDWARE_H__

View File

@@ -0,0 +1,60 @@
/*
* This file is part of the chronometer project.
* Copyright 2019 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "flash.h"
#include "lidar.h"
#include "usart.h"
uint16_t last_lidar_dist = 0;
uint16_t last_lidar_stren = 0;
uint16_t lidar_triggered_dist = 0;
void parse_lidar_data(char *txt){
static int triggered = 0;
last_lidar_dist = txt[2] | (txt[3] << 8);
last_lidar_stren = txt[4] | (txt[5] << 8);
if(last_lidar_stren < LIDAR_LOWER_STREN) return; // weak signal
if(!lidar_triggered_dist){ // first run
lidar_triggered_dist = last_lidar_dist;
return;
}
if(triggered){ // check if body gone
if(last_lidar_dist < the_conf.dist_min || last_lidar_dist > the_conf.dist_max || last_lidar_dist > lidar_triggered_dist + LIDAR_DIST_THRES){
triggered = 0;
#ifdef EBUG
SEND("Untriggered! distance=");
printu(1, last_lidar_dist);
SEND(" signal=");
printu(1, last_lidar_stren);
newline();
#endif
}
}else{
if(last_lidar_dist > the_conf.dist_min && last_lidar_dist < the_conf.dist_max){
triggered = 1;
lidar_triggered_dist = last_lidar_dist;
#ifdef EBUG
SEND("Triggered! distance=");
printu(1, last_lidar_dist);
SEND(" signal=");
printu(1, last_lidar_stren);
newline();
#endif
}
}
}

View File

@@ -0,0 +1,39 @@
/*
* This file is part of the chronometer project.
* Copyright 2019 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef LIDAR_H__
#define LIDAR_H__
#include <stm32f1.h>
#define LIDAR_FRAME_LEN (9)
// frame header
#define LIDAR_FRAME_HEADER (0x59)
// lower strength limit
#define LIDAR_LOWER_STREN (10)
// triggered distance threshold - 1 meter
#define LIDAR_DIST_THRES (100)
#define LIDAR_MIN_DIST (50)
#define LIDAR_MAX_DIST (1000)
extern uint16_t last_lidar_dist;
extern uint16_t lidar_triggered_dist;
extern uint16_t last_lidar_stren;
void parse_lidar_data(char *txt);
#endif // LIDAR_H__

View File

@@ -0,0 +1,272 @@
/*
* main.c
*
* Copyright 2017 Edward V. Emelianoff <eddy@sao.ru, 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 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
//#include "adc.h"
#include "GPS.h"
#include "flash.h"
#include "hardware.h"
#include "lidar.h"
#include "str.h"
#include "time.h"
#include "usart.h"
#include "usb.h"
#include "usb_lib.h"
#ifndef VERSION
#define VERSION "0.0.0"
#endif
// global pseudo-milliseconds counter
volatile uint32_t Tms = 0;
/* Called when systick fires */
void sys_tick_handler(void){
++Tms; // increment pseudo-milliseconds counter
if(++Timer == 1000){ // increment milliseconds counter
time_increment();
}
}
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) */
}
#ifdef EBUG
char *parse_cmd(char *buf){
int32_t N;
static char btns[] = "BTN0=0, BTN1=0, PPS=0\n";
switch(*buf){
case '0':
LED_off();
break;
case '1':
LED_on();
break;
case 'b':
btns[5] = GET_BTN0() + '0';
btns[13] = GET_BTN1() + '0';
btns[20] = GET_PPS() + '0';
return btns;
break;
case 'C':
if(getnum(&buf[1], &N)){
SEND("Need a number!\n");
}else{
addNrecs(N);
}
break;
case 'd':
dump_userconf();
break;
case 'p':
pin_toggle(USBPU_port, USBPU_pin);
SEND("USB pullup is ");
if(pin_read(USBPU_port, USBPU_pin)) SEND("off");
else SEND("on");
newline();
break;
case 'G':
SEND("LIDAR_DIST=");
printu(1, last_lidar_dist);
SEND(", LIDAR_STREN=");
printu(1, last_lidar_stren);
newline();
break;
case 'L':
USB_send("Very long test string for USB (it's length is more than 64 bytes).\n"
"This is another part of the string! Can you see all of this?\n");
return "Long test sent\n";
break;
case 'R':
USB_send("Soft reset\n");
SEND("Soft reset\n");
NVIC_SystemReset();
break;
case 'S':
USB_send("Test string for USB\n");
return "Short test sent\n";
break;
case 'T':
SEND(get_time(&current_time, get_millis()));
break;
case 'W':
USB_send("Wait for reboot\n");
SEND("Wait for reboot\n");
while(1){nop();};
break;
default: // help
if(buf[1] != '\n') return buf;
return
"0/1 - turn on/off LED1\n"
"'b' - get buttons's state\n"
"'d' - dump current user conf\n"
"'p' - toggle USB pullup\n"
"'C' - store userconf for N times\n"
"'G' - get last LIDAR distance\n"
"'L' - send long string over USB\n"
"'R' - software reset\n"
"'S' - send short string over USB\n"
"'T' - show current GPS time\n"
"'W' - test watchdog\n"
;
break;
}
return NULL;
}
#endif
// usb getline
static char *get_USB(){
static char tmpbuf[512], *curptr = tmpbuf;
static int rest = 511;
int x = USB_receive(curptr, rest);
curptr[x] = 0;
if(!x) return NULL;
if(curptr[x-1] == '\n'){
curptr = tmpbuf;
rest = 511;
return tmpbuf;
}
curptr += x; rest -= x;
if(rest <= 0){ // buffer overflow
SEND("USB buffer overflow!\n");
curptr = tmpbuf;
rest = 511;
}
return NULL;
}
int main(void){
uint32_t lastT = 0;
sysreset();
StartHSE();
hw_setup();
LED1_off();
USBPU_OFF();
usarts_setup();
SysTick_Config(SYSTICK_DEFCONF); // function SysTick_Config decrements argument!
SEND("Chronometer version " VERSION ".\n");
if(RCC->CSR & RCC_CSR_IWDGRSTF){ // watchdog reset occured
SEND("WDGRESET=1\n");
}
if(RCC->CSR & RCC_CSR_SFTRSTF){ // software reset occured
SEND("SOFTRESET=1\n");
}
RCC->CSR |= RCC_CSR_RMVF; // remove reset flags
USB_setup();
iwdg_setup();
USBPU_ON();
// read data stored in flash
#ifdef EBUG
SEND("Old config:\n");
dump_userconf();
#endif
//writeatend();
get_userconf();
#ifdef EBUG
SEND("New config:\n");
dump_userconf();
#endif
while (1){
IWDG->KR = IWDG_REFRESH; // refresh watchdog
if(lastT > Tms || Tms - lastT > 499){
if(need2startseq) GPS_send_start_seq();
LED_blink();
if(GPS_status != GPS_VALID) LED1_blink();
else LED1_on();
lastT = Tms;
if(usartrx(LIDAR_USART)){
char *txt;
if(usart_getline(LIDAR_USART, &txt)){
DBG("LIDAR:");
DBG(txt);
}
}
#if defined EBUG || defined USART1PROXY
transmit_tbuf(1); // non-blocking transmission of data from UART buffer every 0.5s
#endif
transmit_tbuf(GPS_USART);
transmit_tbuf(LIDAR_USART);
}
usb_proc();
int r = 0;
char *txt;
if((txt = get_USB())){
DBG("Received data over USB:");
DBG(txt);
if(parse_USBCMD(txt))
USB_send(txt); // echo back non-commands data
}
#if defined EBUG || defined USART1PROXY
if(usartrx(1)){ // usart1 received data, store in in buffer
r = usart_getline(1, &txt);
if(r){
txt[r] = 0;
#ifdef EBUG
char *ans = parse_cmd(txt);
if(ans){
transmit_tbuf(1);
usart_send(1, ans);
transmit_tbuf(1);
}
#else // USART1PROXY - send received data to GPS
usart_send(GPS_USART, txt);
#endif
}
}
#endif
if(usartrx(GPS_USART)){
r = usart_getline(GPS_USART, &txt);
if(r){
txt[r] = 0;
GPS_parse_answer(txt);
}
}
if(usartrx(LIDAR_USART)){
r = usart_getline(LIDAR_USART, &txt);
if(r){
parse_lidar_data(txt);
}
}
}
return 0;
}

View File

@@ -0,0 +1,112 @@
/*
* This file is part of the chronometer project.
* Copyright 2019 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "flash.h"
#include "str.h"
#include "time.h"
#include "usart.h"
#include "usb.h"
/**
* @brief cmpstr - the same as strncmp
* @param s1,s2 - strings to compare
* @param n - max symbols amount
* @return 0 if strings equal or 1/-1
*/
int cmpstr(const char *s1, const char *s2, int n){
int ret = 0;
while(--n){
ret = *s1 - *s2;
if(ret == 0 && *s1 && *s2){
++s1; ++s2;
continue;
}
break;
}
return ret;
}
/**
* @brief getchr - analog of strchr
* @param str - string to search
* @param symbol - searching symbol
* @return pointer to symbol found or NULL
*/
char *getchr(const char *str, char symbol){
do{
if(*str == symbol) return (char*)str;
}while(*(++str));
return NULL;
}
/**
* @brief parse_USBCMD - parsing of string buffer got by USB
* @param cmd - buffer with commands
* @return 0 if got command, 1 if command not recognized
*/
int parse_USBCMD(char *cmd){
#define CMP(a,b) cmpstr(a, b, sizeof(b)-1)
#define GETNUM(x) if(getnum(cmd+sizeof(x)-1, &N)) goto bad_number;
static uint8_t conf_modified = 0;
uint8_t succeed = 0;
int32_t N;
if(!cmd || !*cmd) return 0;
if(*cmd == '?'){ // help
USB_send("Commands:\n"
CMD_DISTMIN " - min distance threshold (cm)\n"
CMD_DISTMAX " - max distance threshold (cm)\n"
CMD_PRINTTIME " - print time\n"
CMD_STORECONF " - store new configuration in flash\n"
);
}else if(CMP(cmd, CMD_PRINTTIME) == 0){
USB_send(get_time(&current_time, get_millis()));
}else if(CMP(cmd, CMD_DISTMIN) == 0){ // set low limit
DBG("CMD_DISTMIN");
GETNUM(CMD_DISTMIN);
if(N < 0 || N > 0xffff) goto bad_number;
if(the_conf.dist_min != (uint16_t)N){
conf_modified = 1;
the_conf.dist_min = (uint16_t) N;
succeed = 1;
}
}else if(CMP(cmd, CMD_DISTMAX) == 0){ // set low limit
DBG("CMD_DISTMAX");
GETNUM(CMD_DISTMAX);
if(N < 0 || N > 0xffff) goto bad_number;
if(the_conf.dist_max != (uint16_t)N){
conf_modified = 1;
the_conf.dist_max = (uint16_t) N;
succeed = 1;
}
}else if(CMP(cmd, CMD_STORECONF) == 0){ // store everything
DBG("Store");
if(conf_modified){
if(store_userconf()){
USB_send("Error: can't save data!\n");
}else{
conf_modified = 0;
succeed = 1;
}
}
}else return 1;
if(succeed) USB_send("Success!\n");
return 0;
bad_number:
USB_send("Error: bad number!\n");
return 0;
}

View File

@@ -0,0 +1,36 @@
/*
* This file is part of the chronometer project.
* Copyright 2019 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef STR_H__
#define STR_H__
// usb commands
// lower and upper limits to capture
#define CMD_DISTMIN "distmin"
#define CMD_DISTMAX "distmax"
#define CMD_ADC1MIN "adc1min"
#define CMD_ADC2MIN "adc2min"
#define CMD_ADC1MAX "adc1max"
#define CMD_ADC2MAX "adc2max"
#define CMD_PRINTTIME "time"
#define CMD_STORECONF "store"
int cmpstr(const char *s1, const char *s2, int n);
char *getchr(const char *str, char symbol);
int parse_USBCMD(char *cmd);
#endif // STR_H__

View File

@@ -0,0 +1,189 @@
/*
* This file is part of the chronometer project.
* Copyright 2019 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "GPS.h"
#include "time.h"
#ifdef EBUG
#include "usart.h"
#endif
#include "usb.h"
#include <string.h>
volatile uint32_t Timer; // milliseconds counter
curtime current_time = TMNOTINI;
// ms counter in last correction by PPS
static uint32_t last_corr_time = 0;
static inline uint8_t atou(const char *b){
return (b[0]-'0')*10 + b[1]-'0';
}
/**
* @brief set_time - set current time from GPS data
* @param buf - buffer with time data (HHMMSS)
*/
void set_time(const char *buf){
uint8_t H = atou(buf) + TIMEZONE_GMT_PLUS;
if(H > 23) H -= 24;
current_time.H = H;
current_time.M = atou(&buf[2]);
current_time.S = atou(&buf[4]);
}
/**
* @brief time_increment - increment system timer by systick
*/
void time_increment(){
Timer = 0;
if(current_time.H == 25) return; // Time not initialized
if(++current_time.S == 60){
current_time.S = 0;
if(++current_time.M == 60){
current_time.M = 0;
if(++current_time.H == 24)
current_time.H = 0;
}
}
#ifdef EBUG
SEND("time_increment(): ");
SEND(get_time(&current_time, 0));
#endif
}
/**
* print time: Tm - time structure, T - milliseconds
*/
char *get_time(curtime *Tm, uint32_t T){
static char buf[64];
char *bstart = &buf[5], *bptr = bstart;
/*
void putint(int i){ // put integer from 0 to 99 into buffer with leading zeros
if(i > 9){
*bptr++ = i/10 + '0';
i = i%10;
}else *bptr++ = '0';
*bptr++ = i + '0';
}*/
int S = 0;
if(Tm->S < 60 && Tm->M < 60 && Tm->H < 24)
S = Tm->S + Tm->H*3600 + Tm->M*60; // seconds from day beginning
if(!S) *(--bstart) = '0';
while(S){
*(--bstart) = S%10 + '0';
S /= 10;
}
// now bstart is buffer starting index; bptr points to decimal point
*bptr++ = '.';
if(T > 99){
*bptr++ = T/100 + '0';
T %= 100;
}else *bptr++ = '0';
if(T > 9){
*bptr++ = T/10 + '0';
T %= 10;
}else *bptr++ = '0';
*bptr++ = T + '0';
if(GPS_status == GPS_NOT_VALID){
strcpy(bptr, " (not valid)");
bptr += 12;
}
if(Tms - last_corr_time > 1000){
strcpy(bptr, " need PPS sync");
bptr += 14;
}
*bptr++ = '\n';
*bptr = 0;
return bstart;
}
/**
* @brief systick_correction
* Makes correction of system timer
* The default frequency of timer is 1kHz - 72000 clocks per interrupt
* So we check how much ticks there was for last one second - between PPS interrupts
* Their amount equal to M = `Timer` value x (SysTick->LOAD+1) + (SysTick->LOAD+1 - SysTick->VAL)
* if `Timer` is very small, add 1000 to its value.
* We need 1000xN ticks instead of M
* if L = LOAD+1, then
* M = Timer*L + L - VAL; newL = L + D = M/1000
* 1000*D = M - 1000*L = L(Timer+1-1000) - VAL ->
* D = [L*(Timer-999) - VAL]/1000
* So correction equal to
* [ (SysTick->LOAD + 1) * (Timer - 999) - SysTick->VAL ] / 1000
*/
void systick_correction(){
SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; // stop systick for a while
int32_t systick_val = SysTick->VAL, L = SysTick->LOAD + 1, timer_val = Timer;
SysTick->VAL = SysTick->LOAD;
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; // start it again
Timer = 0;
if(Tms - last_corr_time < 2000){ // calculate corrections only if Timer was zeroed last time
if(timer_val < 500) timer_val += 1000; // timer already incremented in SysTick interrupt
else time_increment(); // counter less than 1000 -> need to increment time
int32_t D = L * (timer_val - 999) - systick_val;
D /= 1000;
#ifdef EBUG
SEND("Delta: "); if(D < 0){usart_putchar(1, '-'); printu(1, -D);} else printu(1, D); newline();
SEND(get_time(&current_time, 0));
#endif
SysTick->LOAD += D;
}
last_corr_time = Tms;
#if 0
uint32_t t = 0, ticks;
static uint32_t ticksavr = 0, N = 0, last_corr_time = 0;
// correct
int32_t systick_val = SysTick->VAL;
// SysTick->LOAD values for all milliseconds (RVR0) and last millisecond (RVR1)
SysTick->VAL = RVR0;
int32_t timer_val = Timer;
Timer = 0;
// RVR -> SysTick->LOAD
systick_val = SysTick->LOAD + 1 - systick_val; // Systick counts down!
if(timer_val < 10) timer_val += 1000; // our closks go faster than real
else if(timer_val < 990){ // something wrong
RVR0 = RVR1 = SYSTICK_DEFLOAD;
SysTick->LOAD = RVR0;
need_sync = 1;
goto theend;
}else
time_increment(); // ms counter less than 1000 - we need to increment time
t = current_time.H * 3600 + current_time.M * 60 + current_time.S;
if(t - last_corr_time == 1){ // PPS interval == 1s
ticks = systick_val + (timer_val-1)*(RVR0 + 1) + RVR1 + 1;
++N;
ticksavr += ticks;
if(N > 20){
ticks = ticksavr / N;
RVR0 = ticks / 1000 - 1; // main RVR value
SysTick->LOAD = RVR0;
RVR1 = RVR0 + ticks % 1000; // last millisecond RVR value (with fine correction)
N = 0;
ticksavr = 0;
need_sync = 0;
}
}else{
N = 0;
ticksavr = 0;
}
theend:
last_corr_time = t;
#endif
}

View File

@@ -0,0 +1,60 @@
/*
* This file is part of the chronometer project.
* Copyright 2019 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef TIME_H__
#define TIME_H__
#include <stm32f1.h>
// default value for systick_config
#define SYSTICK_DEFCONF (72000)
// defaul for systick->load
#define SYSTICK_DEFLOAD (SYSTICK_DEFCONF - 1)
#define TIMEZONE_GMT_PLUS (3)
#define DIDNT_TRIGGERED (2000)
// debounce delay: .4s
#define TRIGGER_DELAY (400)
#define TMNOTINI {25,61,61}
// current milliseconds
#define get_millis() (Timer)
typedef struct{
uint8_t H;
uint8_t M;
uint8_t S;
} curtime;
extern volatile uint32_t Tms;
extern volatile uint32_t Timer;
extern curtime current_time;
extern curtime trigger_time[];
extern uint32_t trigger_ms[];
extern volatile int need_sync;
char *get_time(curtime *T, uint32_t m);
void set_time(const char *buf);
void time_increment();
void systick_correction();
#endif // TIME_H__

View File

@@ -0,0 +1,365 @@
/*
* This file is part of the chronometer project.
* Copyright 2019 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "stm32f1.h"
#include "usart.h"
#include "lidar.h"
extern volatile uint32_t Tms;
static volatile int idatalen[4][2] = {0}; // received data line length (including '\n')
static volatile int odatalen[4][2] = {0};
volatile int linerdy[4] = {0}, // received data ready
dlen[4] = {0}, // length of data (including '\n') in current buffer
bufovr[4] = {0}, // input buffer overfull
txrdy[4] = {0,1,1,1} // transmission done
;
int rbufno[4] = {0}, tbufno[4] = {0}; // current rbuf/tbuf numbers
static char rbuf[4][2][UARTBUFSZ], tbuf[4][2][UARTBUFSZ]; // receive & transmit buffers
static char *recvdata[4] = {0};
/**
* return length of received data (without trailing zero)
*/
int usart_getline(int n, char **line){
if(bufovr[n]){
bufovr[n] = 0;
linerdy[n] = 0;
return 0;
}
*line = recvdata[n];
linerdy[n] = 0;
return dlen[n];
}
// transmit current tbuf and swap buffers
void transmit_tbuf(int n){
if(n < 1 || n > 3) return;
uint32_t tmout = 72000;
while(!txrdy[n]){if(--tmout == 0) return;}; // wait for previos buffer transmission
register int l = odatalen[n][tbufno[n]];
if(!l) return;
txrdy[n] = 0;
odatalen[n][tbufno[n]] = 0;
DMA_Channel_TypeDef *DMA;
IWDG->KR = IWDG_REFRESH;
switch(n){
case 1:
DMA = DMA1_Channel4;
break;
case 2:
DMA = DMA1_Channel7;
break;
case 3:
DMA = DMA1_Channel2;
break;
}
DMA->CCR &= ~DMA_CCR_EN;
DMA->CMAR = (uint32_t) tbuf[n][tbufno[n]]; // mem
DMA->CNDTR = l;
DMA->CCR |= DMA_CCR_EN;
tbufno[n] = !tbufno[n];
}
void usart_putchar(int n, char ch){
for(int i = 0; odatalen[n][tbufno[n]] == UARTBUFSZ && i < 1024; ++i) transmit_tbuf(n);
tbuf[n][tbufno[n]][odatalen[n][tbufno[n]]++] = ch;
}
void usart_send(int n, const char *str){
uint32_t x = 512;
while(*str && --x){
if(odatalen[n][tbufno[n]] == UARTBUFSZ){
transmit_tbuf(n);
continue;
}
tbuf[n][tbufno[n]][odatalen[n][tbufno[n]]++] = *str++;
}
}
#if defined EBUG || defined USART1PROXY
// only for USART1
void newline(){
usart_putchar(1, '\n');
transmit_tbuf(1);
}
#endif
/*
* USART speed: baudrate = Fck/(USARTDIV)
* USARTDIV stored in USART->BRR
*
* for 72MHz USARTDIV=72000/f(kboud); so for 115200 USARTDIV=72000/115.2=625 -> BRR=0x271
* 9600: BRR = 7500 (0x1D4C)
*/
static void usart_setup(int n, uint32_t BRR){
DMA_Channel_TypeDef *DMA;
IRQn_Type DMAirqN, USARTirqN;
USART_TypeDef *USART;
switch(n){
case 1:
// USART1 Tx DMA - Channel4 (Rx - channel 5)
DMA = DMA1_Channel4;
DMAirqN = DMA1_Channel4_IRQn;
USARTirqN = USART1_IRQn;
// PA9 - Tx, PA10 - Rx
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_USART1EN;
GPIOA->CRH |= CRH(9, CNF_AFPP|MODE_NORMAL) | CRH(10, CNF_FLINPUT|MODE_INPUT);
USART = USART1;
break;
case 2:
// USART2 Tx DMA - Channel7
DMA = DMA1_Channel7;
DMAirqN = DMA1_Channel7_IRQn;
USARTirqN = USART2_IRQn;
// PA2 - Tx, PA3 - Rx
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
GPIOA->CRL |= CRL(2, CNF_AFPP|MODE_NORMAL) | CRL(3, CNF_FLINPUT|MODE_INPUT);
USART = USART2;
break;
case 3:
// USART3 Tx DMA - Channel2
DMA = DMA1_Channel2;
DMAirqN = DMA1_Channel2_IRQn;
USARTirqN = USART3_IRQn;
// PB10 - Tx, PB11 - Rx
RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
RCC->APB1ENR |= RCC_APB1ENR_USART3EN;
GPIOB->CRH |= CRH(10, CNF_AFPP|MODE_NORMAL) | CRH(11, CNF_FLINPUT|MODE_INPUT);
USART = USART3;
break;
default:
return;
}
DMA->CPAR = (uint32_t) &USART->DR; // periph
DMA->CCR |= DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TCIE; // 8bit, mem++, mem->per, transcompl irq
// setup usart(n)
USART->BRR = BRR;
USART->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; // 1start,8data,nstop; enable Rx,Tx,USART
uint32_t tmout = 16000000;
while(!(USART->SR & USART_SR_TC)){if(--tmout == 0) break;} // polling idle frame Transmission
USART->SR = 0; // clear flags
USART->CR1 |= USART_CR1_RXNEIE; // allow Rx IRQ
USART->CR3 = USART_CR3_DMAT; // enable DMA Tx
// Tx CNDTR set @ each transmission due to data size
NVIC_SetPriority(DMAirqN, n);
NVIC_EnableIRQ(DMAirqN);
NVIC_SetPriority(USARTirqN, n);
NVIC_EnableIRQ(USARTirqN);
}
void usarts_setup(){
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
#if defined EBUG || defined USART1PROXY
usart_setup(1, 72000000 / 115200); // debug console or GPS proxy
#endif
usart_setup(2, 36000000 / 9600); // GPS
usart_setup(3, 36000000 / 115200); // LIDAR
}
void usart_isr(int n, USART_TypeDef *USART){
#ifdef CHECK_TMOUT
static uint32_t tmout[n] = 0;
#endif
IWDG->KR = IWDG_REFRESH;
if(USART->SR & USART_SR_RXNE){ // RX not emty - receive next char
#ifdef CHECK_TMOUT
if(tmout[n] && Tms >= tmout[n]){ // set overflow flag
bufovr[n] = 1;
idatalen[n][rbufno[n]] = 0;
}
tmout[n] = Tms + TIMEOUT_MS;
if(!tmout[n]) tmout[n] = 1; // prevent 0
#endif
uint8_t rb = USART->DR;
if(idatalen[n][rbufno[n]] < UARTBUFSZ){ // put next char into buf
rbuf[n][rbufno[n]][idatalen[n][rbufno[n]]++] = rb;
if(rb == '\n'){ // got newline - line ready
linerdy[n] = 1;
dlen[n] = idatalen[n][rbufno[n]];
recvdata[n] = rbuf[n][rbufno[n]];
// prepare other buffer
rbufno[n] = !rbufno[n];
idatalen[n][rbufno[n]] = 0;
#ifdef CHECK_TMOUT
// clear timeout at line end
tmout[n] = 0;
#endif
}
}else{ // buffer overrun
bufovr[n] = 1;
idatalen[n][rbufno[n]] = 0;
#ifdef CHECK_TMOUT
tmout[n] = 0;
#endif
}
}
}
#if defined EBUG || defined USART1PROXY
void usart1_isr(){
usart_isr(1, USART1);
}
#endif
// GPS_USART
void usart2_isr(){
usart_isr(2, USART2);
}
// LIDAR_USART
void usart3_isr(){
IWDG->KR = IWDG_REFRESH;
if(USART3->SR & USART_SR_RXNE){ // RX not emty - receive next char
uint8_t rb = USART3->DR, L = idatalen[3][rbufno[3]];
if(rb != LIDAR_FRAME_HEADER && (L == 0 || L == 1)){ // bad starting sequence
idatalen[3][rbufno[3]] = 0;
return;
}
if(L < LIDAR_FRAME_LEN){ // put next char into buf
rbuf[3][rbufno[3]][idatalen[3][rbufno[3]]++] = rb;
if(L == LIDAR_FRAME_LEN-1){ // got LIDAR_FRAME_LEN bytes - line ready
linerdy[3] = 1;
dlen[3] = idatalen[3][rbufno[3]];
recvdata[3] = rbuf[3][rbufno[3]];
// prepare other buffer
rbufno[3] = !rbufno[3];
idatalen[3][rbufno[3]] = 0;
}
}else{ // buffer overrun
idatalen[3][rbufno[3]] = 0;
}
}
}
// return string buffer with val
char *u2str(uint32_t val){
static char bufa[11];
char bufb[10];
int l = 0, bpos = 0;
IWDG->KR = IWDG_REFRESH;
if(!val){
bufa[0] = '0';
l = 1;
}else{
while(val){
bufb[l++] = val % 10 + '0';
val /= 10;
}
int i;
bpos += l;
for(i = 0; i < l; ++i){
bufa[--bpos] = bufb[i];
}
}
bufa[l + bpos] = 0;
return bufa;
}
// print 32bit unsigned int
void printu(int n, uint32_t val){
usart_send(n, u2str(val));
}
// print 32bit unsigned int as hex
void printuhex(int n, uint32_t val){
usart_send(n, "0x");
uint8_t *ptr = (uint8_t*)&val + 3;
int i, j;
IWDG->KR = IWDG_REFRESH;
for(i = 0; i < 4; ++i, --ptr){
for(j = 1; j > -1; --j){
register uint8_t half = (*ptr >> (4*j)) & 0x0f;
if(half < 10) usart_putchar(n, half + '0');
else usart_putchar(n, half - 10 + 'a');
}
}
}
#ifdef EBUG
// dump memory buffer
void hexdump(uint8_t *arr, uint16_t len){
for(uint16_t l = 0; l < len; ++l, ++arr){
IWDG->KR = IWDG_REFRESH;
for(int16_t j = 1; j > -1; --j){
register uint8_t half = (*arr >> (4*j)) & 0x0f;
if(half < 10) usart_putchar(1, half + '0');
else usart_putchar(1, half - 10 + 'a');
}
if(l % 16 == 15) usart_putchar(1, '\n');
else if((l & 3) == 3) usart_putchar(1, ' ');
}
}
#endif
#if defined EBUG || defined USART1PROXY
void dma1_channel4_isr(){ // USART1
if(DMA1->ISR & DMA_ISR_TCIF4){ // Tx
DMA1->IFCR = DMA_IFCR_CTCIF4; // clear TC flag
txrdy[1] = 1;
}
}
#endif
void dma1_channel7_isr(){ // USART2
if(DMA1->ISR & DMA_ISR_TCIF7){ // Tx
DMA1->IFCR = DMA_IFCR_CTCIF7; // clear TC flag
txrdy[2] = 1;
}
}
void dma1_channel2_isr(){ // USART3
if(DMA1->ISR & DMA_ISR_TCIF2){ // Tx
DMA1->IFCR = DMA_IFCR_CTCIF2; // clear TC flag
txrdy[3] = 1;
}
}
// read `buf` and get first integer `N` in it
// @return 0 if all OK or 1 if there's not a number; omit spaces and '='
int getnum(const char *buf, int32_t *N){
char c;
int positive = -1;
int32_t val = 0;
while((c = *buf++)){
if(c == '\t' || c == ' ' || c == '='){
if(positive < 0) continue; // beginning spaces
else break; // spaces after number
}
if(c == '-'){
if(positive < 0){
positive = 0;
continue;
}else break; // there already was `-` or number
}
if(c < '0' || c > '9') break;
if(positive < 0) positive = 1;
val = val * 10 + (int32_t)(c - '0');
}
if(positive != -1){
if(positive == 0){
if(val == 0) return 1; // single '-'
val = -val;
}
*N = val;
}else return 1;
return 0;
}

View File

@@ -0,0 +1,67 @@
/*
* This file is part of the chronometer project.
* Copyright 2019 Edward V. Emelianov <edward.emelianoff@gmail.com>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#ifndef __USART_H__
#define __USART_H__
#include <stm32f1.h>
// input and output buffers size
#define UARTBUFSZ (128)
// timeout between data bytes
#ifndef TIMEOUT_MS
#define TIMEOUT_MS (1500)
#endif
#define STR_HELPER(s) #s
#define STR(s) STR_HELPER(s)
#ifdef EBUG
#define SEND(str) usart_send(1, str)
#define MSG(str) do{SEND(__FILE__ " (L" STR(__LINE__) "): " str);}while(0)
#define DBG(str) do{SEND(str); newline(); }while(0)
#else
#define SEND(str)
#define MSG(str)
#define DBG(str)
#endif
#define usartrx(n) (linerdy[n])
#define usartovr(n) (bufovr[n])
extern volatile int linerdy[4], bufovr[4], txrdy[4];
void transmit_tbuf(int n);
void usarts_setup();
int usart_getline(int n, char **line);
void usart_send(int n, const char *str);
void usart_putchar(int n, char ch);
char *u2str(uint32_t val);
void printu(int n, uint32_t val);
void printuhex(int n, uint32_t val);
int getnum(const char *buf, int32_t *N);
#if defined EBUG || defined USART1PROXY
void newline();
#endif
#ifdef EBUG
void hexdump(uint8_t *arr, uint16_t len);
#endif
#endif // __USART_H__

View File

@@ -0,0 +1,157 @@
/*
* geany_encoding=koi8-r
* usb.c - base functions for different USB types
*
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, 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 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#include "usb.h"
#include "usb_lib.h"
#include "usart.h"
// incoming buffer size
#define IDATASZ (256)
static uint8_t incoming_data[IDATASZ];
static uint8_t ovfl = 0;
static uint16_t idatalen = 0;
static volatile uint8_t tx_succesfull = 0;
static int8_t usbON = 0; // ==1 when USB fully configured
// interrupt IN handler (never used?)
static uint16_t EP1_Handler(ep_t ep){
if (ep.rx_flag){
ep.status = SET_VALID_TX(ep.status);
ep.status = KEEP_STAT_RX(ep.status);
}else if (ep.tx_flag){
ep.status = SET_VALID_RX(ep.status);
ep.status = SET_STALL_TX(ep.status);
}
return ep.status;
}
// data IN/OUT handler
static uint16_t EP23_Handler(ep_t ep){
if(ep.rx_flag){
int rd = ep.rx_cnt, rest = IDATASZ - idatalen;
if(rd){
if(rd <= rest){
idatalen += EP_Read(2, (uint16_t*)&incoming_data[idatalen]);
ovfl = 0;
}else{
ep.status = SET_NAK_RX(ep.status);
ovfl = 1;
return ep.status;
}
}
// end of transaction: clear DTOGs
ep.status = CLEAR_DTOG_RX(ep.status);
ep.status = CLEAR_DTOG_TX(ep.status);
ep.status = SET_STALL_TX(ep.status);
}else if (ep.tx_flag){
ep.status = KEEP_STAT_TX(ep.status);
tx_succesfull = 1;
}
ep.status = SET_VALID_RX(ep.status);
return ep.status;
}
void USB_setup(){
NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn);
NVIC_DisableIRQ(USB_HP_CAN1_TX_IRQn);
RCC->APB1ENR |= RCC_APB1ENR_USBEN;
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);
NVIC_EnableIRQ(USB_HP_CAN1_TX_IRQn );
}
void usb_proc(){
if(USB_GetState() == USB_CONFIGURE_STATE){ // USB configured - activate other endpoints
if(!usbON){ // endpoints not activated
// make new BULK endpoint
// Buffer have 1024 bytes, but last 256 we use for CAN bus (30.2 of RM: USB main features)
EP_Init(1, EP_TYPE_INTERRUPT, 10, 0, EP1_Handler); // IN1 - transmit
EP_Init(2, EP_TYPE_BULK, 0, USB_RXBUFSZ, EP23_Handler); // OUT2 - receive data
EP_Init(3, EP_TYPE_BULK, USB_TXBUFSZ, 0, EP23_Handler); // IN3 - transmit data
usbON = 1;
}
}else{
usbON = 0;
}
}
void USB_send(char *buf){
if(!USB_configured()){
DBG("USB not configured");
return;
}
uint16_t l = 0, ctr = 0;
char *p = buf;
while(*p++) ++l;
while(l){
uint16_t s = (l > USB_TXBUFSZ) ? USB_TXBUFSZ : l;
tx_succesfull = 0;
EP_Write(3, (uint8_t*)&buf[ctr], s);
uint32_t ctra = 1000000;
while(--ctra && tx_succesfull == 0);
l -= s;
ctr += s;
}
}
/**
* @brief USB_receive
* @param buf (i) - buffer for received data
* @param bufsize - its size
* @return amount of received bytes
*/
int USB_receive(char *buf, int bufsize){
if(!bufsize || !idatalen) return 0;
USB->CNTR = 0;
int sz = (idatalen > bufsize) ? bufsize : idatalen, rest = idatalen - sz;
for(int i = 0; i < sz; ++i) buf[i] = incoming_data[i];
if(rest > 0){
uint8_t *ptr = &incoming_data[sz];
for(int i = 0; i < rest; ++i) incoming_data[i] = *ptr++;
//memmove(incoming_data, &incoming_data[sz], rest); - hardfault on memcpy&memmove
idatalen = rest;
}else idatalen = 0;
if(ovfl){
EP23_Handler(endpoints[2]);
uint16_t epstatus = USB->EPnR[2];
epstatus = CLEAR_DTOG_RX(epstatus);
epstatus = SET_VALID_RX(epstatus);
USB->EPnR[2] = epstatus;
}
USB->CNTR = USB_CNTR_RESETM | USB_CNTR_CTRM;
return sz;
}
/**
* @brief USB_configured
* @return 1 if USB is in configured state
*/
int USB_configured(){
return usbON;
}

View File

@@ -0,0 +1,37 @@
/*
* geany_encoding=koi8-r
* usb.h
*
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, 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 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#pragma once
#ifndef __USB_H__
#define __USB_H__
#include "hardware.h"
#define BUFFSIZE (64)
void USB_setup();
void usb_proc();
void USB_send(char *buf);
int USB_receive(char *buf, int bufsize);
int USB_configured();
#endif // __USB_H__

View File

@@ -0,0 +1,117 @@
/*
* geany_encoding=koi8-r
* usb_defs.h
*
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, 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 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#pragma once
#ifndef __USB_DEFS_H__
#define __USB_DEFS_H__
#include <stm32f1.h>
// max endpoints number
#define STM32ENDPOINTS 8
/**
* Buffers size definition
**/
#define USB_BTABLE_SIZE 512
// first 64 bytes of USB_BTABLE are registers!
//#define USB_EP0_BASEADDR 64
// for USB FS EP0 buffers are from 8 to 64 bytes long (64 for PL2303)
#define USB_EP0_BUFSZ 64
// USB transmit buffer size (64 for PL2303)
#define USB_TXBUFSZ 64
// USB receive buffer size (64 for PL2303)
#define USB_RXBUFSZ 64
#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
#ifdef USB_TypeDef
#define USB_TypeDef USB_TypeDef_custom
#endif
typedef struct {
__IO uint32_t EPnR[STM32ENDPOINTS];
__IO uint32_t RESERVED[STM32ENDPOINTS];
__IO uint32_t CNTR;
__IO uint32_t ISTR;
__IO uint32_t FNR;
__IO uint32_t DADDR;
__IO uint32_t BTABLE;
} USB_TypeDef;
/*
typedef struct{
__IO uint16_t USB_ADDR_TX;
__IO uint16_t res1;
__IO uint16_t USB_COUNT_TX;
__IO uint16_t res2;
__IO uint16_t USB_ADDR_RX;
__IO uint16_t res3;
__IO uint16_t USB_COUNT_RX;
__IO uint16_t res4;
} USB_EPDATA_TypeDef;*/
typedef struct{
__IO uint32_t USB_ADDR_TX;
__IO uint32_t USB_COUNT_TX;
__IO uint32_t USB_ADDR_RX;
__IO uint32_t USB_COUNT_RX;
} USB_EPDATA_TypeDef;
typedef struct{
__IO USB_EPDATA_TypeDef EP[STM32ENDPOINTS];
} USB_BtableDef;
#endif // __USB_DEFS_H__

View File

@@ -0,0 +1,521 @@
/*
* geany_encoding=koi8-r
* usb_lib.c
*
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, 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 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#include <stdint.h>
#include "usb_lib.h"
#include "usart.h"
ep_t endpoints[STM32ENDPOINTS];
static usb_dev_t USB_Dev;
static usb_LineCoding lineCoding = {115200, 0, 0, 8};
config_pack_t setup_packet;
static uint8_t ep0databuf[EP0DATABUF_SIZE];
static uint8_t ep0dbuflen = 0;
usb_LineCoding getLineCoding(){return lineCoding;}
// definition of parts common for USB_DeviceDescriptor & USB_DeviceQualifierDescriptor
#define bcdUSB_L 0x10
#define bcdUSB_H 0x01
#define bDeviceClass 0
#define bDeviceSubClass 0
#define bDeviceProtocol 0
#define bNumConfigurations 1
static const uint8_t USB_DeviceDescriptor[] = {
18, // bLength
0x01, // bDescriptorType - Device descriptor
bcdUSB_L, // bcdUSB_L - 1.10
bcdUSB_H, // bcdUSB_H
bDeviceClass, // bDeviceClass - USB_COMM
bDeviceSubClass, // bDeviceSubClass
bDeviceProtocol, // bDeviceProtocol
USB_EP0_BUFSZ, // bMaxPacketSize
0x7b, // idVendor_L PL2303: VID=0x067b, PID=0x2303
0x06, // idVendor_H
0x03, // idProduct_L
0x23, // idProduct_H
0x00, // bcdDevice_Ver_L
0x03, // bcdDevice_Ver_H
0x01, // iManufacturer
0x02, // iProduct
0x00, // iSerialNumber
bNumConfigurations // bNumConfigurations
};
static const uint8_t USB_DeviceQualifierDescriptor[] = {
10, //bLength
0x06, // bDescriptorType - Device qualifier
bcdUSB_L, // bcdUSB_L
bcdUSB_H, // bcdUSB_H
bDeviceClass, // bDeviceClass
bDeviceSubClass, // bDeviceSubClass
bDeviceProtocol, // bDeviceProtocol
USB_EP0_BUFSZ, // bMaxPacketSize0
bNumConfigurations, // bNumConfigurations
0x00 // Reserved
};
static const uint8_t USB_ConfigDescriptor[] = {
/*Configuration Descriptor*/
0x09, /* bLength: Configuration Descriptor size */
0x02, /* bDescriptorType: Configuration */
39, /* wTotalLength:no of returned bytes */
0x00,
0x01, /* bNumInterfaces: 1 interface */
0x01, /* bConfigurationValue: Configuration value */
0x00, /* iConfiguration: Index of string descriptor describing the configuration */
0xa0, /* bmAttributes - Bus powered, Remote wakeup */
0x32, /* MaxPower 100 mA */
/*---------------------------------------------------------------------------*/
/*Interface Descriptor */
0x09, /* bLength: Interface Descriptor size */
0x04, /* bDescriptorType: Interface */
0x00, /* bInterfaceNumber: Number of Interface */
0x00, /* bAlternateSetting: Alternate setting */
0x03, /* bNumEndpoints: 3 endpoints used */
0xff, /* bInterfaceClass */
0x00, /* bInterfaceSubClass */
0x00, /* bInterfaceProtocol */
0x00, /* iInterface: */
///////////////////////////////////////////////////
/*Endpoint 1 Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
0x05, /* bDescriptorType: Endpoint */
0x81, /* bEndpointAddress IN1 */
0x03, /* bmAttributes: Interrupt */
0x0a, /* wMaxPacketSize LO: */
0x00, /* wMaxPacketSize HI: */
0x01, /* bInterval: */
/*Endpoint OUT2 Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
0x05, /* bDescriptorType: Endpoint */
0x02, /* bEndpointAddress: OUT2 */
0x02, /* bmAttributes: Bulk */
(USB_RXBUFSZ & 0xff), /* wMaxPacketSize: 64 */
(USB_RXBUFSZ >> 8),
0x00, /* bInterval: ignore for Bulk transfer */
/*Endpoint IN3 Descriptor*/
0x07, /* bLength: Endpoint Descriptor size */
0x05, /* bDescriptorType: Endpoint */
0x83, /* bEndpointAddress IN3 */
0x02, /* bmAttributes: Bulk */
(USB_TXBUFSZ & 0xff), /* wMaxPacketSize: 64 */
(USB_TXBUFSZ >> 8),
0x00, /* bInterval: ignore for Bulk transfer */
};
_USB_LANG_ID_(USB_StringLangDescriptor, LANG_US);
// these descriptors are not used in PL2303 emulator!
_USB_STRING_(USB_StringSerialDescriptor, u"0");
_USB_STRING_(USB_StringManufacturingDescriptor, u"Prolific Technology Inc.");
_USB_STRING_(USB_StringProdDescriptor, u"USB-Serial Controller");
/*
* default handlers
*/
// SET_LINE_CODING
void WEAK linecoding_handler(usb_LineCoding __attribute__((unused)) *lc){
}
// SET_CONTROL_LINE_STATE
void WEAK clstate_handler(uint16_t __attribute__((unused)) val){
}
// SEND_BREAK
void WEAK break_handler(){
}
// handler of vendor requests
void WEAK vendor_handler(config_pack_t *packet){
if(packet->bmRequestType & 0x80){ // read
uint8_t c;
switch(packet->wValue){
case 0x8484:
c = 2;
break;
case 0x0080:
c = 1;
break;
case 0x8686:
c = 0xaa;
break;
default:
c = 0;
}
EP_WriteIRQ(0, &c, 1);
}else{ // write ZLP
EP_WriteIRQ(0, (uint8_t *)0, 0);
}
}
static void wr0(const uint8_t *buf, uint16_t size){
if(setup_packet.wLength < size) size = setup_packet.wLength;
EP_WriteIRQ(0, buf, size);
}
static inline void get_descriptor(){
switch(setup_packet.wValue){
case DEVICE_DESCRIPTOR:
wr0(USB_DeviceDescriptor, sizeof(USB_DeviceDescriptor));
break;
case CONFIGURATION_DESCRIPTOR:
wr0(USB_ConfigDescriptor, sizeof(USB_ConfigDescriptor));
break;
case STRING_LANG_DESCRIPTOR:
wr0((const uint8_t *)&USB_StringLangDescriptor, STRING_LANG_DESCRIPTOR_SIZE_BYTE);
break;
case STRING_MAN_DESCRIPTOR:
wr0((const uint8_t *)&USB_StringManufacturingDescriptor, USB_StringManufacturingDescriptor.bLength);
break;
case STRING_PROD_DESCRIPTOR:
wr0((const uint8_t *)&USB_StringProdDescriptor, USB_StringProdDescriptor.bLength);
break;
case STRING_SN_DESCRIPTOR:
wr0((const uint8_t *)&USB_StringSerialDescriptor, USB_StringSerialDescriptor.bLength);
break;
case DEVICE_QUALIFIER_DESCRIPTOR:
wr0(USB_DeviceQualifierDescriptor, USB_DeviceQualifierDescriptor[0]);
break;
default:
break;
}
}
static uint8_t configuration = 0; // reply for GET_CONFIGURATION (==1 if configured)
static inline void std_d2h_req(){
uint16_t status = 0; // bus powered
switch(setup_packet.bRequest){
case GET_DESCRIPTOR:
get_descriptor();
break;
case GET_STATUS:
EP_WriteIRQ(0, (uint8_t *)&status, 2); // send status: Bus Powered
break;
case GET_CONFIGURATION:
EP_WriteIRQ(0, &configuration, 1);
break;
default:
break;
}
}
static inline void std_h2d_req(){
switch(setup_packet.bRequest){
case SET_ADDRESS:
// new address will be assigned later - after acknowlegement or request to host
USB_Dev.USB_Addr = setup_packet.wValue;
break;
case SET_CONFIGURATION:
// Now device configured
USB_Dev.USB_Status = USB_CONFIGURE_STATE;
configuration = setup_packet.wValue;
break;
default:
break;
}
}
/*
bmRequestType: 76543210
7 direction: 0 - host->device, 1 - device->host
65 type: 0 - standard, 1 - class, 2 - vendor
4..0 getter: 0 - device, 1 - interface, 2 - endpoint, 3 - other
*/
/**
* Endpoint0 (control) handler
* @param ep - endpoint state
* @return data written to EP0R
*/
static uint16_t EP0_Handler(ep_t ep){
uint16_t epstatus = ep.status; // EP0R on input -> return this value after modifications
uint8_t reqtype = setup_packet.bmRequestType & 0x7f;
uint8_t dev2host = (setup_packet.bmRequestType & 0x80) ? 1 : 0;
if ((ep.rx_flag) && (ep.setup_flag)){
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);
}
epstatus = SET_NAK_RX(epstatus);
epstatus = SET_VALID_TX(epstatus);
break;
case STANDARD_ENDPOINT_REQUEST_TYPE: // standard endpoint request
if(setup_packet.bRequest == CLEAR_FEATURE){
EP_WriteIRQ(0, (uint8_t *)0, 0);
epstatus = SET_NAK_RX(epstatus);
epstatus = SET_VALID_TX(epstatus);
}
break;
case VENDOR_REQUEST_TYPE:
vendor_handler(&setup_packet);
epstatus = SET_NAK_RX(epstatus);
epstatus = SET_VALID_TX(epstatus);
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:
clstate_handler(setup_packet.wValue);
break;
case SEND_BREAK:
break_handler();
break;
default:
break;
}
if(!dev2host) EP_WriteIRQ(0, (uint8_t *)0, 0); // write acknowledgement
epstatus = SET_VALID_RX(epstatus);
epstatus = SET_VALID_TX(epstatus);
break;
default:
EP_WriteIRQ(0, (uint8_t *)0, 0);
epstatus = SET_NAK_RX(epstatus);
epstatus = SET_VALID_TX(epstatus);
}
}else if (ep.rx_flag){ // got data over EP0 or host acknowlegement
if(ep.rx_cnt){
//EP_WriteIRQ(0, (uint8_t *)0, 0);
if(setup_packet.bRequest == SET_LINE_CODING){
linecoding_handler((usb_LineCoding*)ep0databuf);
}
}
// wait for new data from host
epstatus = SET_VALID_RX(epstatus);
epstatus = SET_VALID_TX(epstatus);
} else if (ep.tx_flag){ // package transmitted
// now we can change address after enumeration
if ((USB->DADDR & USB_DADDR_ADD) != USB_Dev.USB_Addr){
USB->DADDR = USB_DADDR_EF | USB_Dev.USB_Addr;
// change state to ADRESSED
USB_Dev.USB_Status = USB_ADRESSED_STATE;
}
// end of transaction
epstatus = CLEAR_DTOG_RX(epstatus);
epstatus = CLEAR_DTOG_TX(epstatus);
epstatus = SET_VALID_RX(epstatus);
epstatus = SET_VALID_TX(epstatus);
}
return epstatus;
}
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, uint16_t (*func)(ep_t ep)){
if(number >= STM32ENDPOINTS) return 4; // out of configured amount
if(txsz > USB_BTABLE_SIZE || rxsz > USB_BTABLE_SIZE) return 1; // buffer too large
if(lastaddr + txsz + rxsz >= USB_BTABLE_SIZE) return 2; // out of btable
USB->EPnR[number] = (type << 9) | (number & USB_EPnR_EA);
USB->EPnR[number] ^= USB_EPnR_STAT_RX | USB_EPnR_STAT_TX_1;
if(rxsz & 1 || rxsz > 512) return 3; // wrong rx buffer size
uint16_t countrx = 0;
if(rxsz < 64) countrx = rxsz / 2;
else{
if(rxsz & 0x1f) return 3; // should be multiple of 32
countrx = 31 + rxsz / 32;
}
USB_BTABLE->EP[number].USB_ADDR_TX = lastaddr;
endpoints[number].tx_buf = (uint16_t *)(USB_BTABLE_BASE + lastaddr*2);
lastaddr += txsz;
USB_BTABLE->EP[number].USB_COUNT_TX = 0;
USB_BTABLE->EP[number].USB_ADDR_RX = lastaddr;
endpoints[number].rx_buf = (uint16_t *)(USB_BTABLE_BASE + lastaddr*2);
lastaddr += rxsz;
USB_BTABLE->EP[number].USB_COUNT_RX = countrx << 10;
endpoints[number].func = func;
return 0;
}
//extern int8_t dump;
// standard IRQ handler
void usb_isr(){
if (USB->ISTR & USB_ISTR_RESET){
// Reinit registers
USB->CNTR = USB_CNTR_RESETM | USB_CNTR_CTRM;
USB->ISTR = 0;
// Endpoint 0 - CONTROL
// ON USB LS size of EP0 may be 8 bytes, but on FS it should be 64 bytes!
lastaddr = LASTADDR_DEFAULT;
if(EP_Init(0, EP_TYPE_CONTROL, USB_EP0_BUFSZ, USB_EP0_BUFSZ, EP0_Handler)){
DBG("Err init EP0");
}
// clear address, leave only enable bit
USB->DADDR = USB_DADDR_EF;
// state is default - wait for enumeration
USB_Dev.USB_Status = USB_DEFAULT_STATE;
}
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];
// dump = 1;
// Calculate flags
endpoints[n].rx_flag = (epstatus & USB_EPnR_CTR_RX) ? 1 : 0;
endpoints[n].setup_flag = (epstatus & USB_EPnR_SETUP) ? 1 : 0;
endpoints[n].tx_flag = (epstatus & USB_EPnR_CTR_TX) ? 1 : 0;
// copy received bytes amount
endpoints[n].rx_cnt = USB_BTABLE->EP[n].USB_COUNT_RX & 0x3FF; // low 10 bits is counter
// check direction
if(USB->ISTR & USB_ISTR_DIR){ // OUT interrupt - receive data, CTR_RX==1 (if CTR_TX == 1 - two pending transactions: receive following by transmit)
if(n == 0){ // control endpoint
if(epstatus & USB_EPnR_SETUP){ // setup packet -> copy data to conf_pack
EP_Read(0, (uint16_t*)&setup_packet);
ep0dbuflen = 0;
// interrupt handler will be called later
}else if(epstatus & USB_EPnR_CTR_RX){ // data packet -> push received data to ep0databuf
ep0dbuflen = endpoints[0].rx_cnt;
EP_Read(0, (uint16_t*)&ep0databuf);
}
}
}else{ // IN interrupt - transmit data, only CTR_TX == 1
// enumeration end could be here (if EP0)
}
// prepare status field for EP handler
endpoints[n].status = epstatus;
// call EP handler (even if it will change EPnR, it should return new status)
epstatus = endpoints[n].func(endpoints[n]);
// keep DTOG state
epstatus = KEEP_DTOG_TX(epstatus);
epstatus = KEEP_DTOG_RX(epstatus);
// clear all RX/TX flags
epstatus = CLEAR_CTR_RX(epstatus);
epstatus = CLEAR_CTR_TX(epstatus);
// refresh EPnR
USB->EPnR[n] = epstatus;
}
}
/*
if (USB->ISTR & USB_ISTR_PMAOVR) {
MSG("PMAOVR\n");
// Handle PMAOVR status
}
if (USB->ISTR & USB_ISTR_SUSP) {
MSG("SUSP\n");
if (USB->DADDR & 0x7f) {
USB->DADDR = 0;
USB->CNTR &= ~ 0x800;
}
}
if (USB->ISTR & USB_ISTR_ERR) {
MSG("ERR\n");
// Handle Error
}
if (USB->ISTR & USB_ISTR_WKUP) {
MSG("WKUP\n");
// Handle Wakeup
}
if (USB->ISTR & USB_ISTR_SOF) {
MSG("SOF\n");
// Handle SOF
}
if (USB->ISTR & USB_ISTR_ESOF) {
MSG("ESOF\n");
// Handle ESOF
}
USB->ISTR = 0;
*/
void usb_lp_can_rx0_isr(){
usb_isr();
}
void usb_hp_can_tx_isr(){
usb_isr();
}
/**
* 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){
uint8_t i;
if(size > USB_TXBUFSZ) size = USB_TXBUFSZ;
uint16_t N2 = (size + 1) >> 1;
// the buffer is 16-bit, so we should copy data as it would be uint16_t
uint16_t *buf16 = (uint16_t *)buf;
uint32_t *out = (uint32_t *)endpoints[number].tx_buf;
for(i = 0; i < N2; ++i, ++out){
*out = buf16[i];
}
USB_BTABLE->EP[number].USB_COUNT_TX = size;
}
/**
* Write data to EP buffer (called outside IRQ handler)
* @param number - EP number
* @param *buf - array with data
* @param size - its size
*/
void EP_Write(uint8_t number, const uint8_t *buf, uint16_t size){
uint16_t status = USB->EPnR[number];
EP_WriteIRQ(number, buf, size);
status = SET_NAK_RX(status);
status = SET_VALID_TX(status);
status = KEEP_DTOG_TX(status);
status = KEEP_DTOG_RX(status);
USB->EPnR[number] = status;
}
/*
* Copy data from EP buffer into user buffer area
* @param *buf - user array for data
* @return amount of data read
*/
int EP_Read(uint8_t number, uint16_t *buf){
int n = (endpoints[number].rx_cnt + 1) >> 1;
uint32_t *in = (uint32_t *)endpoints[number].rx_buf;
if(n){
for(int i = 0; i < n; ++i, ++in)
buf[i] = *(uint16_t*)in;
}
return endpoints[number].rx_cnt;
}
// USB status
uint8_t USB_GetState(){
return USB_Dev.USB_Status;
}

View File

@@ -0,0 +1,202 @@
/*
* geany_encoding=koi8-r
* usb_lib.h
*
* Copyright 2018 Edward V. Emelianov <eddy@sao.ru, 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 2 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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
*/
#pragma once
#ifndef __USB_LIB_H__
#define __USB_LIB_H__
#include <wchar.h>
#include "usb_defs.h"
#define EP0DATABUF_SIZE (64)
#define LASTADDR_DEFAULT (STM32ENDPOINTS * 8)
// Max EP amount (EP0 + other used)
//#define ENDPOINTS_NUM 4
// bmRequestType & 0x7f
#define STANDARD_DEVICE_REQUEST_TYPE 0
#define STANDARD_ENDPOINT_REQUEST_TYPE 2
#define VENDOR_REQUEST_TYPE 0x40
#define CONTROL_REQUEST_TYPE 0x21
// bRequest, standard; for bmRequestType == 0x80
#define GET_STATUS 0x00
#define GET_DESCRIPTOR 0x06
#define GET_CONFIGURATION 0x08
// for bmRequestType == 0
#define CLEAR_FEATURE 0x01
#define SET_FEATURE 0x03 // unused
#define SET_ADDRESS 0x05
#define SET_DESCRIPTOR 0x07 // unused
#define SET_CONFIGURATION 0x09
// for bmRequestType == 0x81, 1 or 0xB2
#define GET_INTERFACE 0x0A // unused
#define SET_INTERFACE 0x0B // unused
#define SYNC_FRAME 0x0C // unused
#define VENDOR_REQUEST 0x01 // unused
// Class-Specific Control Requests
#define SEND_ENCAPSULATED_COMMAND 0x00 // unused
#define GET_ENCAPSULATED_RESPONSE 0x01 // unused
#define SET_COMM_FEATURE 0x02 // unused
#define GET_COMM_FEATURE 0x03 // unused
#define CLEAR_COMM_FEATURE 0x04 // unused
#define SET_LINE_CODING 0x20
#define GET_LINE_CODING 0x21
#define SET_CONTROL_LINE_STATE 0x22
#define SEND_BREAK 0x23
// control line states
#define CONTROL_DTR 0x01
#define CONTROL_RTS 0x02
// wValue
#define DEVICE_DESCRIPTOR 0x100
#define CONFIGURATION_DESCRIPTOR 0x200
#define STRING_LANG_DESCRIPTOR 0x300
#define STRING_MAN_DESCRIPTOR 0x301
#define STRING_PROD_DESCRIPTOR 0x302
#define STRING_SN_DESCRIPTOR 0x303
#define DEVICE_QUALIFIER_DESCRIPTOR 0x600
// EPnR bits manipulation
#define CLEAR_DTOG_RX(R) (R & USB_EPnR_DTOG_RX) ? R : (R & (~USB_EPnR_DTOG_RX))
#define SET_DTOG_RX(R) (R & USB_EPnR_DTOG_RX) ? (R & (~USB_EPnR_DTOG_RX)) : R
#define TOGGLE_DTOG_RX(R) (R | USB_EPnR_DTOG_RX)
#define KEEP_DTOG_RX(R) (R & (~USB_EPnR_DTOG_RX))
#define CLEAR_DTOG_TX(R) (R & USB_EPnR_DTOG_TX) ? R : (R & (~USB_EPnR_DTOG_TX))
#define SET_DTOG_TX(R) (R & USB_EPnR_DTOG_TX) ? (R & (~USB_EPnR_DTOG_TX)) : R
#define TOGGLE_DTOG_TX(R) (R | USB_EPnR_DTOG_TX)
#define KEEP_DTOG_TX(R) (R & (~USB_EPnR_DTOG_TX))
#define SET_VALID_RX(R) ((R & USB_EPnR_STAT_RX) ^ USB_EPnR_STAT_RX) | (R & (~USB_EPnR_STAT_RX))
#define SET_NAK_RX(R) ((R & USB_EPnR_STAT_RX) ^ USB_EPnR_STAT_RX_1) | (R & (~USB_EPnR_STAT_RX))
#define SET_STALL_RX(R) ((R & USB_EPnR_STAT_RX) ^ USB_EPnR_STAT_RX_0) | (R & (~USB_EPnR_STAT_RX))
#define KEEP_STAT_RX(R) (R & (~USB_EPnR_STAT_RX))
#define SET_VALID_TX(R) ((R & USB_EPnR_STAT_TX) ^ USB_EPnR_STAT_TX) | (R & (~USB_EPnR_STAT_TX))
#define SET_NAK_TX(R) ((R & USB_EPnR_STAT_TX) ^ USB_EPnR_STAT_TX_1) | (R & (~USB_EPnR_STAT_TX))
#define SET_STALL_TX(R) ((R & USB_EPnR_STAT_TX) ^ USB_EPnR_STAT_TX_0) | (R & (~USB_EPnR_STAT_TX))
#define KEEP_STAT_TX(R) (R & (~USB_EPnR_STAT_TX))
#define CLEAR_CTR_RX(R) (R & (~USB_EPnR_CTR_RX))
#define CLEAR_CTR_TX(R) (R & (~USB_EPnR_CTR_TX))
#define CLEAR_CTR_RX_TX(R) (R & (~(USB_EPnR_CTR_TX | USB_EPnR_CTR_RX)))
// USB state: uninitialized, addressed, ready for use
#define USB_DEFAULT_STATE 0
#define USB_ADRESSED_STATE 1
#define USB_CONFIGURE_STATE 2
// EP types
#define EP_TYPE_BULK 0x00
#define EP_TYPE_CONTROL 0x01
#define EP_TYPE_ISO 0x02
#define EP_TYPE_INTERRUPT 0x03
#define LANG_US (uint16_t)0x0409
#define _USB_STRING_(name, str) \
static const struct name \
{ \
uint8_t bLength; \
uint8_t bDescriptorType; \
uint16_t bString[(sizeof(str) - 2) / 2]; \
\
} \
name = {sizeof(name), 0x03, str}
#define _USB_LANG_ID_(name, lng_id) \
\
static const struct name \
{ \
uint8_t bLength; \
uint8_t bDescriptorType; \
uint16_t bString; \
\
} \
name = {0x04, 0x03, lng_id}
#define STRING_LANG_DESCRIPTOR_SIZE_BYTE (4)
// EP0 configuration packet
typedef struct {
uint8_t bmRequestType;
uint8_t bRequest;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
} config_pack_t;
// endpoints state
typedef struct __ep_t{
uint16_t *tx_buf; // transmission buffer address
uint16_t *rx_buf; // reception buffer address
uint16_t (*func)(); // endpoint action function
uint16_t status; // status flags
unsigned rx_cnt : 10; // received data counter
unsigned tx_flag : 1; // transmission flag
unsigned rx_flag : 1; // reception flag
unsigned setup_flag : 1; // this is setup packet (only for EP0)
} ep_t;
// USB status & its address
typedef struct {
uint8_t USB_Status;
uint16_t USB_Addr;
}usb_dev_t;
typedef struct {
uint32_t dwDTERate;
uint8_t bCharFormat;
#define USB_CDC_1_STOP_BITS 0
#define USB_CDC_1_5_STOP_BITS 1
#define USB_CDC_2_STOP_BITS 2
uint8_t bParityType;
#define USB_CDC_NO_PARITY 0
#define USB_CDC_ODD_PARITY 1
#define USB_CDC_EVEN_PARITY 2
#define USB_CDC_MARK_PARITY 3
#define USB_CDC_SPACE_PARITY 4
uint8_t bDataBits;
} __attribute__ ((packed)) usb_LineCoding;
typedef struct {
uint8_t bmRequestType;
uint8_t bNotificationType;
uint16_t wValue;
uint16_t wIndex;
uint16_t wLength;
} __attribute__ ((packed)) usb_cdc_notification;
extern ep_t endpoints[];
void USB_Init();
uint8_t USB_GetState();
int EP_Init(uint8_t number, uint8_t type, uint16_t txsz, uint16_t rxsz, uint16_t (*func)(ep_t ep));
void EP_WriteIRQ(uint8_t number, const uint8_t *buf, uint16_t size);
void EP_Write(uint8_t number, const uint8_t *buf, uint16_t size);
int EP_Read(uint8_t number, uint16_t *buf);
usb_LineCoding getLineCoding();
void WEAK linecoding_handler(usb_LineCoding *lc);
void WEAK clstate_handler(uint16_t val);
void WEAK break_handler();
void WEAK vendor_handler(config_pack_t *packet);
#endif // __USB_LIB_H__