Repo re-org
This commit is contained in:
parent
312fee491c
commit
50998fcfb9
270 changed files with 9 additions and 9 deletions
6
hardware/fw/.gitignore
vendored
Normal file
6
hardware/fw/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
*.expand
|
||||
*.map
|
||||
*.lst
|
||||
*.hex
|
||||
*.elf
|
||||
*.bin
|
||||
111
hardware/fw/Makefile
Normal file
111
hardware/fw/Makefile
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
# Megumin LED display firmware
|
||||
# Copyright (C) 2018 Sebastian Götte <code@jaseg.net>
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
CUBE_PATH ?= $(wildcard ~)/resource/STM32CubeF0
|
||||
CMSIS_PATH ?= $(CUBE_PATH)/Drivers/CMSIS
|
||||
CMSIS_DEV_PATH ?= $(CMSIS_PATH)/Device/ST/STM32F0xx
|
||||
HAL_PATH ?= $(CUBE_PATH)/Drivers/STM32F0xx_HAL_Driver
|
||||
|
||||
MAC_ADDR ?= 0xdeadbeef
|
||||
|
||||
CC := arm-none-eabi-gcc
|
||||
LD := arm-none-eabi-ld
|
||||
OBJCOPY := arm-none-eabi-objcopy
|
||||
OBJDUMP := arm-none-eabi-objdump
|
||||
SIZE := arm-none-eabi-size
|
||||
|
||||
CFLAGS = -g -Wall -Wextra -std=gnu11 -O0 -fdump-rtl-expand
|
||||
CFLAGS += -mlittle-endian -mcpu=cortex-m0 -march=armv6-m -mthumb
|
||||
#CFLAGS += -ffunction-sections -fdata-sections
|
||||
LDFLAGS = -nostartfiles
|
||||
#LDFLAGS += -specs=rdimon.specs -DSEMIHOSTING
|
||||
LDFLAGS += -Wl,-Map=main.map -nostdlib
|
||||
#LDFLAGS += -Wl,--gc-sections
|
||||
LIBS = -lgcc
|
||||
#LIBS += -lrdimon
|
||||
|
||||
# Technically we're using an STM32F030F4, but apart from the TSSOP20 package that one is largely identical to the
|
||||
# STM32F030*6 and there is no separate device header provided for it, so we're faking a *6 device here. This is
|
||||
# even documented in stm32f0xx.h. Thanks ST!
|
||||
CFLAGS += -DSTM32F030x6 -DHSE_VALUE=19440000
|
||||
|
||||
LDFLAGS += -Tstm32_flash.ld
|
||||
CFLAGS += -I$(CMSIS_DEV_PATH)/Include -I$(CMSIS_PATH)/Include -I$(HAL_PATH)/Inc -Iconfig -Wno-unused -I../common
|
||||
LDFLAGS += -L$(CMSIS_PATH)/Lib/GCC -larm_cortexM0l_math
|
||||
|
||||
###################################################
|
||||
|
||||
.PHONY: program clean
|
||||
|
||||
all: main.elf
|
||||
|
||||
.clang:
|
||||
echo flags = $(CFLAGS) > .clang
|
||||
|
||||
cmsis_exports.c: $(CMSIS_DEV_PATH)/Include/stm32f030x6.h $(CMSIS_PATH)/Include/core_cm0.h
|
||||
python3 tools/gen_cmsis_exports.py $^ > $@
|
||||
|
||||
%.o: %.c
|
||||
$(CC) -c $(CFLAGS) -o $@ $^
|
||||
# $(CC) -E $(CFLAGS) -o $(@:.o=.pp) $^
|
||||
|
||||
%.o: %.s
|
||||
$(CC) -c $(CFLAGS) -o $@ $^
|
||||
# $(CC) -E $(CFLAGS) -o $(@:.o=.pp) $^
|
||||
|
||||
%.dot: %.elf
|
||||
r2 -a arm -qc 'aa;agC' $< 2>/dev/null >$@
|
||||
|
||||
sources.tar.xz: main.c Makefile
|
||||
tar -caf $@ $^
|
||||
|
||||
# don't ask...
|
||||
sources.tar.xz.zip: sources.tar.xz
|
||||
zip $@ $^
|
||||
|
||||
sources.c: sources.tar.xz.zip
|
||||
xxd -i $< | head -n -1 | sed 's/=/__attribute__((section(".source_tarball"))) =/' > $@
|
||||
|
||||
main.elf: main.c adc.c serial.c cobs.c startup_stm32f030x6.s system_stm32f0xx.c $(HAL_PATH)/Src/stm32f0xx_ll_utils.c base.c cmsis_exports.c
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS)
|
||||
$(OBJCOPY) -O ihex $@ $(@:.elf=.hex)
|
||||
$(OBJCOPY) -O binary $@ $(@:.elf=.bin)
|
||||
$(OBJDUMP) -St $@ >$(@:.elf=.lst)
|
||||
$(SIZE) $@
|
||||
|
||||
program: main.elf openocd.cfg
|
||||
openocd -f openocd.cfg -c "program $< verify reset exit"
|
||||
|
||||
8b10b_test_encode: 8b10b_test_encode.c 8b10b.c
|
||||
gcc -o $@ $^
|
||||
|
||||
8b10b_test_decode: 8b10b_test_decode.c 8b10b.c
|
||||
gcc -o $@ $^
|
||||
|
||||
protocol_test: protocol.c protocol_test.c
|
||||
gcc -o $@ -O0 -Wall -Wextra -g -I../common $^
|
||||
|
||||
clean:
|
||||
rm -f **.o
|
||||
rm -f main.elf main.hex main.bin main.map main.lst
|
||||
rm -f **.expand
|
||||
rm -f cmsis_exports.c
|
||||
rm -f sources.tar.xz
|
||||
rm -f sources.tar.xz.zip
|
||||
rm -f sources.c
|
||||
rm -f *.dot
|
||||
rm -f protocol_test
|
||||
|
||||
906
hardware/fw/Scope.ipynb
Normal file
906
hardware/fw/Scope.ipynb
Normal file
File diff suppressed because one or more lines are too long
136
hardware/fw/adc.c
Normal file
136
hardware/fw/adc.c
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
/* Megumin LED display firmware
|
||||
* Copyright (C) 2018 Sebastian Götte <code@jaseg.net>
|
||||
*
|
||||
* 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 "serial.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
static struct __attribute__((__packed__)) hl_adc_pkt {
|
||||
struct ll_pkt ll;
|
||||
uint16_t seq;
|
||||
int32_t gps_1pps_period_sysclk;
|
||||
volatile uint16_t data[32];
|
||||
} adc_pkt[2];
|
||||
static uint16_t current_seq = 0;
|
||||
static int current_buf = 0;
|
||||
|
||||
static void adc_dma_init(void);
|
||||
static void adc_dma_launch(void);
|
||||
|
||||
|
||||
/* Mode that can be used for debugging */
|
||||
void adc_init() {
|
||||
adc_dma_init();
|
||||
|
||||
/* Clock from PCLK/4 instead of the internal exclusive high-speed RC oscillator. */
|
||||
ADC1->CFGR2 = (2<<ADC_CFGR2_CKMODE_Pos); /* Use PCLK/4=12MHz */
|
||||
/* Sampling time 239.5 ADC clock cycles -> total conversion time 38.5us*/
|
||||
ADC1->SMPR = (7<<ADC_SMPR_SMP_Pos);
|
||||
|
||||
/* Setup DMA and triggering */
|
||||
/* Trigger from TIM1 TRGO */
|
||||
ADC1->CFGR1 = ADC_CFGR1_DMAEN | ADC_CFGR1_DMACFG | (2<<ADC_CFGR1_EXTEN_Pos) | (1<<ADC_CFGR1_EXTSEL_Pos);
|
||||
ADC1->CHSELR = ADC_CHSELR_CHSEL2;
|
||||
/* Perform self-calibration */
|
||||
ADC1->CR |= ADC_CR_ADCAL;
|
||||
while (ADC1->CR & ADC_CR_ADCAL)
|
||||
;
|
||||
/* Enable conversion */
|
||||
ADC1->CR |= ADC_CR_ADEN;
|
||||
ADC1->CR |= ADC_CR_ADSTART;
|
||||
}
|
||||
|
||||
static void adc_dma_init() {
|
||||
/* Configure DMA 1 Channel 1 to get rid of all the data */
|
||||
DMA1_Channel1->CPAR = (unsigned int)&ADC1->DR;
|
||||
DMA1_Channel1->CCR = (0<<DMA_CCR_PL_Pos);
|
||||
DMA1_Channel1->CCR |=
|
||||
(1<<DMA_CCR_MSIZE_Pos) /* 16 bit */
|
||||
| (1<<DMA_CCR_PSIZE_Pos) /* 16 bit */
|
||||
| DMA_CCR_MINC
|
||||
| DMA_CCR_TCIE; /* Enable transfer complete interrupt. */
|
||||
|
||||
/* triggered on half-transfer and on transfer completion. We use this to send out the ADC data and to trap into GDB. */
|
||||
NVIC_EnableIRQ(DMA1_Channel1_IRQn);
|
||||
NVIC_SetPriority(DMA1_Channel1_IRQn, 2<<5);
|
||||
|
||||
adc_dma_launch();
|
||||
}
|
||||
|
||||
void adc_dma_launch() {
|
||||
DMA1_Channel1->CCR &= ~DMA_CCR_EN; /* Disable channel */
|
||||
current_buf = !current_buf;
|
||||
DMA1_Channel1->CMAR = (unsigned int)&(adc_pkt[current_buf].data);
|
||||
DMA1_Channel1->CNDTR = ARRAY_LEN(adc_pkt[current_buf].data);
|
||||
DMA1_Channel1->CCR |= DMA_CCR_EN; /* Enable channel */
|
||||
}
|
||||
|
||||
void adc_timer_init(int psc, int ivl) {
|
||||
TIM1->BDTR = TIM_BDTR_MOE; /* MOE is needed even though we only "output" a chip-internal signal */
|
||||
TIM1->CCMR2 = (6<<TIM_CCMR2_OC4M_Pos); /* PWM Mode 1 to get a clean trigger signal */
|
||||
TIM1->CCER = TIM_CCER_CC4E; /* Enable capture/compare unit 4 connected to ADC */
|
||||
TIM1->CCR4 = 1; /* Trigger at start of timer cycle */
|
||||
/* Set prescaler and interval */
|
||||
TIM1->PSC = psc-1;
|
||||
TIM1->ARR = ivl-1;
|
||||
/* Preload all values */
|
||||
TIM1->EGR = TIM_EGR_UG;
|
||||
TIM1->CR1 = TIM_CR1_ARPE;
|
||||
/* And... go! */
|
||||
TIM1->CR1 |= TIM_CR1_CEN;
|
||||
}
|
||||
|
||||
/* This acts as a no-op that provides a convenient point to set a breakpoint for the debug scope logic */
|
||||
static void gdb_dump(void) {
|
||||
}
|
||||
|
||||
void DMA1_Channel1_IRQHandler(void) {
|
||||
uint32_t isr = DMA1->ISR;
|
||||
/* Clear the interrupt flag */
|
||||
DMA1->IFCR |= DMA_IFCR_CGIF1;
|
||||
adc_dma_launch();
|
||||
|
||||
gdb_dump();
|
||||
|
||||
adc_pkt[!current_buf].seq = current_seq++;
|
||||
adc_pkt[!current_buf].gps_1pps_period_sysclk = gps_1pps_period_sysclk;
|
||||
/* Ignore return value since we can't do anything here. Overruns are logged in serial.c. */
|
||||
usart_send_packet_nonblocking(&adc_pkt[!current_buf].ll, sizeof(adc_pkt[!current_buf]));
|
||||
|
||||
/*
|
||||
static int debug_buf_pos = 0;
|
||||
if (st->sync) {
|
||||
if (debug_buf_pos < NCH) {
|
||||
debug_buf_pos = NCH;
|
||||
} else {
|
||||
adc_buf[debug_buf_pos++] = symbol;
|
||||
|
||||
if (debug_buf_pos >= sizeof(adc_buf)/sizeof(adc_buf[0])) {
|
||||
debug_buf_pos = 0;
|
||||
st->sync = 0;
|
||||
gdb_dump();
|
||||
for (int i=0; i<sizeof(adc_buf)/sizeof(adc_buf[0]); i++)
|
||||
adc_buf[i] = -255;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
26
hardware/fw/adc.h
Normal file
26
hardware/fw/adc.h
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/* Megumin LED display firmware
|
||||
* Copyright (C) 2018 Sebastian Götte <code@jaseg.net>
|
||||
*
|
||||
* 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 "global.h"
|
||||
|
||||
void adc_init();
|
||||
void adc_timer_init(int psc, int ivl);
|
||||
|
||||
#endif/*__ADC_H__*/
|
||||
25
hardware/fw/base.c
Normal file
25
hardware/fw/base.c
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
int __errno = 0;
|
||||
void *_impure_ptr = NULL;
|
||||
|
||||
void __sinit(void) {
|
||||
}
|
||||
|
||||
void *memset(void *s, int c, size_t n) {
|
||||
char *end = (char *)s + n;
|
||||
for (char *p = (char *)s; p < end; p++)
|
||||
*p = (char)c;
|
||||
return s;
|
||||
}
|
||||
|
||||
size_t strlen(const char *s) {
|
||||
const char *start = s;
|
||||
while (*s++);
|
||||
return s - start - 1;
|
||||
}
|
||||
|
||||
void __assert_func(bool value) {
|
||||
}
|
||||
48
hardware/fw/cmsis_exports.c
Normal file
48
hardware/fw/cmsis_exports.c
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#ifndef __GENERATED_CMSIS_HEADER_EXPORTS__
|
||||
#define __GENERATED_CMSIS_HEADER_EXPORTS__
|
||||
|
||||
#include <stm32f030x6.h>
|
||||
|
||||
/* stm32f030x6.h */
|
||||
TIM_TypeDef *tim3 = TIM3;
|
||||
TIM_TypeDef *tim14 = TIM14;
|
||||
RTC_TypeDef *rtc = RTC;
|
||||
WWDG_TypeDef *wwdg = WWDG;
|
||||
IWDG_TypeDef *iwdg = IWDG;
|
||||
I2C_TypeDef *i2c1 = I2C1;
|
||||
PWR_TypeDef *pwr = PWR;
|
||||
SYSCFG_TypeDef *syscfg = SYSCFG;
|
||||
EXTI_TypeDef *exti = EXTI;
|
||||
ADC_TypeDef *adc1 = ADC1;
|
||||
ADC_Common_TypeDef *adc1_common = ADC1_COMMON;
|
||||
ADC_Common_TypeDef *adc = ADC;
|
||||
TIM_TypeDef *tim1 = TIM1;
|
||||
SPI_TypeDef *spi1 = SPI1;
|
||||
USART_TypeDef *usart1 = USART1;
|
||||
TIM_TypeDef *tim16 = TIM16;
|
||||
TIM_TypeDef *tim17 = TIM17;
|
||||
DBGMCU_TypeDef *dbgmcu = DBGMCU;
|
||||
DMA_TypeDef *dma1 = DMA1;
|
||||
DMA_Channel_TypeDef *dma1_channel1 = DMA1_Channel1;
|
||||
DMA_Channel_TypeDef *dma1_channel2 = DMA1_Channel2;
|
||||
DMA_Channel_TypeDef *dma1_channel3 = DMA1_Channel3;
|
||||
DMA_Channel_TypeDef *dma1_channel4 = DMA1_Channel4;
|
||||
DMA_Channel_TypeDef *dma1_channel5 = DMA1_Channel5;
|
||||
FLASH_TypeDef *flash = FLASH;
|
||||
OB_TypeDef *ob = OB;
|
||||
RCC_TypeDef *rcc = RCC;
|
||||
CRC_TypeDef *crc = CRC;
|
||||
GPIO_TypeDef *gpioa = GPIOA;
|
||||
GPIO_TypeDef *gpiob = GPIOB;
|
||||
GPIO_TypeDef *gpioc = GPIOC;
|
||||
GPIO_TypeDef *gpiod = GPIOD;
|
||||
GPIO_TypeDef *gpiof = GPIOF;
|
||||
|
||||
#include <core_cm0.h>
|
||||
|
||||
/* core_cm0.h */
|
||||
SCB_Type *scb = SCB;
|
||||
SysTick_Type *systick = SysTick;
|
||||
NVIC_Type *nvic = NVIC;
|
||||
|
||||
#endif//__GENERATED_CMSIS_HEADER_EXPORTS__
|
||||
212
hardware/fw/cobs.c
Normal file
212
hardware/fw/cobs.c
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
|
||||
#include "serial.h"
|
||||
#include "cobs.h"
|
||||
|
||||
int cobs_encode_usart(int (*output)(char), char *src, size_t srclen) {
|
||||
if (srclen > 254)
|
||||
return -1;
|
||||
|
||||
size_t p = 0;
|
||||
while (p <= srclen) {
|
||||
|
||||
char val;
|
||||
if (p != 0 && src[p-1] != 0) {
|
||||
val = src[p-1];
|
||||
|
||||
} else {
|
||||
size_t q = p;
|
||||
while (q < srclen && src[q] != 0)
|
||||
q++;
|
||||
val = (char)q-p+1;
|
||||
}
|
||||
|
||||
int rv = output(val);
|
||||
if (rv)
|
||||
return rv;
|
||||
p++;
|
||||
}
|
||||
|
||||
int rv = output(0);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*@ requires \valid(dst + (0..dstlen-1));
|
||||
@ requires \valid_read(src + (0..srclen-1));
|
||||
@ requires \separated(dst + (0..dstlen-1), src + (0..srclen-1));
|
||||
@
|
||||
@ behavior maybe_valid_frame:
|
||||
@ assumes 1 <= srclen <= dstlen <= 65535;
|
||||
@ assumes \exists integer j; j > 0 && \forall integer i; 0 <= i < j ==> src[i] != 0;
|
||||
@ assumes \exists integer i; 0 <= i < srclen && src[i] == 0;
|
||||
@ assigns dst[0..dstlen-1];
|
||||
@ ensures \result >= 0 || \result == -3;
|
||||
@ ensures \result >= 0 ==> src[\result+1] == 0;
|
||||
@ ensures \result >= 0 ==> (\forall integer i; 0 <= i < \result ==> src[i] != 0);
|
||||
@
|
||||
@ behavior invalid_frame:
|
||||
@ assumes 1 <= srclen <= dstlen <= 65535;
|
||||
@ assumes src[0] == 0 || \forall integer i; 0 <= i < srclen ==> src[i] != 0;
|
||||
@ assigns dst[0..dstlen-1];
|
||||
@ ensures \result == -2;
|
||||
@
|
||||
@ behavior invalid_buffers:
|
||||
@ assumes dstlen < 0 || dstlen > 65535
|
||||
@ || srclen < 1 || srclen > 65535
|
||||
@ || dstlen < srclen;
|
||||
@ assigns \nothing;
|
||||
@ ensures \result == -1;
|
||||
@
|
||||
@ complete behaviors;
|
||||
@ disjoint behaviors;
|
||||
@*/
|
||||
ssize_t cobs_decode(char *dst, size_t dstlen, char *src, size_t srclen) {
|
||||
if (dstlen > 65535 || srclen > 65535)
|
||||
return -1;
|
||||
|
||||
if (srclen < 1)
|
||||
return -1;
|
||||
|
||||
if (dstlen < srclen)
|
||||
return -1;
|
||||
|
||||
size_t p = 1;
|
||||
size_t c = (unsigned char)src[0];
|
||||
//@ assert 0 <= c < 256;
|
||||
//@ assert 0 <= c;
|
||||
//@ assert c < 256;
|
||||
if (c == 0)
|
||||
return -2; /* invalid framing. An empty frame would be [...] 00 01 00, not [...] 00 00 */
|
||||
//@ assert c >= 0;
|
||||
//@ assert c != 0;
|
||||
//@ assert c <= 257;
|
||||
//@ assert c > 0;
|
||||
//@ assert c >= 0 && c != 0 ==> c > 0;
|
||||
|
||||
/*@ //loop invariant \forall integer i; 0 <= i <= p ==> (i == srclen || src[i] != 0);
|
||||
@ loop invariant \forall integer i; 1 <= i < p ==> src[i] != 0;
|
||||
@ loop invariant c > 0;
|
||||
@ loop invariant 1 <= p <= srclen <= dstlen <= 65535;
|
||||
@ loop invariant \separated(dst + (0..dstlen-1), src + (0..srclen-1));
|
||||
@ loop invariant \valid_read(src + (0..srclen-1));
|
||||
@ loop invariant \forall integer i; 1 <= i <= srclen ==> \valid(dst + i - 1);
|
||||
@ loop assigns dst[0..dstlen-1], p, c;
|
||||
@ loop variant srclen-p;
|
||||
@*/
|
||||
while (p < srclen && src[p]) {
|
||||
char val;
|
||||
c--;
|
||||
|
||||
//@ assert src[p] != 0;
|
||||
if (c == 0) {
|
||||
c = (unsigned char)src[p];
|
||||
val = 0;
|
||||
} else {
|
||||
val = src[p];
|
||||
}
|
||||
|
||||
//@ assert 0 <= p-1 <= dstlen-1;
|
||||
dst[p-1] = val;
|
||||
p++;
|
||||
}
|
||||
|
||||
if (p == srclen)
|
||||
return -2; /* Invalid framing. The terminating null byte should always be present in the input buffer. */
|
||||
|
||||
if (c != 1)
|
||||
return -3; /* Invalid framing. The skip counter does not hit the end of the frame. */
|
||||
|
||||
//@ assert 0 < p <= srclen <= 65535;
|
||||
//@ assert src[p] == 0;
|
||||
//@ assert \forall integer i; 1 <= i < p ==> src[i] != 0;
|
||||
return p-1;
|
||||
}
|
||||
|
||||
void cobs_decode_incremental_initialize(struct cobs_decode_state *state) {
|
||||
state->p = 0;
|
||||
state->c = 0;
|
||||
}
|
||||
|
||||
int cobs_decode_incremental(struct cobs_decode_state *state, char *dst, size_t dstlen, char src) {
|
||||
if (state->p == 0) {
|
||||
if (src == 0)
|
||||
goto empty_errout; /* invalid framing. An empty frame would be [...] 00 01 00, not [...] 00 00 */
|
||||
state->c = (unsigned char)src;
|
||||
state->p++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!src) {
|
||||
if (state->c != 1)
|
||||
goto errout; /* Invalid framing. The skip counter does not hit the end of the frame. */
|
||||
int rv = state->p-1;
|
||||
cobs_decode_incremental_initialize(state);
|
||||
return rv;
|
||||
}
|
||||
|
||||
char val;
|
||||
state->c--;
|
||||
|
||||
if (state->c == 0) {
|
||||
state->c = (unsigned char)src;
|
||||
val = 0;
|
||||
} else {
|
||||
val = src;
|
||||
}
|
||||
|
||||
size_t pos = state->p-1;
|
||||
if (pos >= dstlen)
|
||||
return -2; /* output buffer too small */
|
||||
dst[pos] = val;
|
||||
state->p++;
|
||||
return 0;
|
||||
|
||||
errout:
|
||||
cobs_decode_incremental_initialize(state);
|
||||
return -1;
|
||||
|
||||
empty_errout:
|
||||
cobs_decode_incremental_initialize(state);
|
||||
return -3;
|
||||
}
|
||||
|
||||
#ifdef VALIDATION
|
||||
/*@
|
||||
@ requires 0 <= d < 256;
|
||||
@ assigns \nothing;
|
||||
@*/
|
||||
size_t test(char foo, unsigned int d) {
|
||||
unsigned int c = (unsigned char)foo;
|
||||
if (c != 0) {
|
||||
//@ assert c < 256;
|
||||
//@ assert c >= 0;
|
||||
//@ assert c != 0;
|
||||
//@ assert c > 0;
|
||||
}
|
||||
if (d != 0) {
|
||||
//@ assert d >= 0;
|
||||
//@ assert d != 0;
|
||||
//@ assert d > 0;
|
||||
}
|
||||
return c + d;
|
||||
}
|
||||
|
||||
#include <__fc_builtin.h>
|
||||
|
||||
void main(void) {
|
||||
char inbuf[254];
|
||||
char cobsbuf[256];
|
||||
char outbuf[256];
|
||||
|
||||
size_t range = Frama_C_interval(0, sizeof(inbuf));
|
||||
Frama_C_make_unknown((char *)inbuf, range);
|
||||
|
||||
cobs_encode(cobsbuf, sizeof(cobsbuf), inbuf, sizeof(inbuf));
|
||||
cobs_decode(outbuf, sizeof(outbuf), cobsbuf, sizeof(cobsbuf));
|
||||
|
||||
//@ assert \forall integer i; 0 <= i < sizeof(inbuf) ==> outbuf[i] == inbuf[i];
|
||||
}
|
||||
#endif//VALIDATION
|
||||
|
||||
23
hardware/fw/cobs.h
Normal file
23
hardware/fw/cobs.h
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#ifndef __COBS_H__
|
||||
#define __COBS_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
struct cobs_decode_state {
|
||||
size_t p;
|
||||
size_t c;
|
||||
};
|
||||
|
||||
|
||||
ssize_t cobs_encode(char *dst, size_t dstlen, char *src, size_t srclen);
|
||||
ssize_t cobs_decode(char *dst, size_t dstlen, char *src, size_t srclen);
|
||||
|
||||
int cobs_encode_usart(int (*output)(char), char *src, size_t srclen);
|
||||
|
||||
void cobs_decode_incremental_initialize(struct cobs_decode_state *state);
|
||||
int cobs_decode_incremental(struct cobs_decode_state *state, char *dst, size_t dstlen, char src);
|
||||
|
||||
#endif//__COBS_H__
|
||||
79
hardware/fw/crctest.py
Normal file
79
hardware/fw/crctest.py
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
custom_crc_table = {}
|
||||
|
||||
def generate_crc32_table(_poly):
|
||||
|
||||
global custom_crc_table
|
||||
|
||||
for i in range(256):
|
||||
c = i << 24
|
||||
|
||||
for j in range(8):
|
||||
c = (c << 1) ^ _poly if (c & 0x80000000) else c << 1
|
||||
|
||||
custom_crc_table[i] = c & 0xffffffff
|
||||
|
||||
|
||||
def crc32_stm(bytes_arr):
|
||||
|
||||
length = len(bytes_arr)
|
||||
crc = 0xffffffff
|
||||
|
||||
k = 0
|
||||
while length >= 4:
|
||||
|
||||
v = ((bytes_arr[k] << 24) & 0xFF000000) | ((bytes_arr[k+1] << 16) & 0xFF0000) | \
|
||||
((bytes_arr[k+2] << 8) & 0xFF00) | (bytes_arr[k+3] & 0xFF)
|
||||
|
||||
crc = ((crc << 8) & 0xffffffff) ^ custom_crc_table[0xFF & ((crc >> 24) ^ v)]
|
||||
crc = ((crc << 8) & 0xffffffff) ^ custom_crc_table[0xFF & ((crc >> 24) ^ (v >> 8))]
|
||||
crc = ((crc << 8) & 0xffffffff) ^ custom_crc_table[0xFF & ((crc >> 24) ^ (v >> 16))]
|
||||
crc = ((crc << 8) & 0xffffffff) ^ custom_crc_table[0xFF & ((crc >> 24) ^ (v >> 24))]
|
||||
|
||||
k += 4
|
||||
length -= 4
|
||||
|
||||
if length > 0:
|
||||
v = 0
|
||||
|
||||
for i in range(length):
|
||||
v |= (bytes_arr[k+i] << 24-i*8)
|
||||
|
||||
if length == 1:
|
||||
v &= 0xFF000000
|
||||
|
||||
elif length == 2:
|
||||
v &= 0xFFFF0000
|
||||
|
||||
elif length == 3:
|
||||
v &= 0xFFFFFF00
|
||||
|
||||
crc = (( crc << 8 ) & 0xffffffff) ^ custom_crc_table[0xFF & ( (crc >> 24) ^ (v ) )];
|
||||
crc = (( crc << 8 ) & 0xffffffff) ^ custom_crc_table[0xFF & ( (crc >> 24) ^ (v >> 8) )];
|
||||
crc = (( crc << 8 ) & 0xffffffff) ^ custom_crc_table[0xFF & ( (crc >> 24) ^ (v >> 16) )];
|
||||
crc = (( crc << 8 ) & 0xffffffff) ^ custom_crc_table[0xFF & ( (crc >> 24) ^ (v >> 24) )];
|
||||
|
||||
return crc
|
||||
|
||||
poly = 0x04C11DB7
|
||||
buf = bytes(reversed([1, 2, 3, 4]))
|
||||
|
||||
generate_crc32_table(poly)
|
||||
print(hex(crc32_stm(bytearray(buf))))
|
||||
|
||||
from crccheck import crc
|
||||
import struct
|
||||
|
||||
def rev_bits_in_word(w):
|
||||
return sum( ((w>>i)&1) << (31-i) for i in range(32) )
|
||||
|
||||
import zlib
|
||||
def crc32stm(inbytes):
|
||||
crc32 = crc.Crc32.calc(inbytes)^0xffffffff
|
||||
#crc32 = zlib.crc32(inbytes)^0xffffffff
|
||||
crc32 = rev_bits_in_word(crc32)
|
||||
return crc32
|
||||
|
||||
#data = [0x80,0x40,0xc0,0x20]
|
||||
data = [0x00, 0, 0, 0x80, 0, 0, 0, 0x80]
|
||||
print(hex(crc32stm(bytes(data))))
|
||||
print(hex(zlib.crc32(bytes([0, 0, 0, 1]))^0xffffffff))
|
||||
62
hardware/fw/global.h
Normal file
62
hardware/fw/global.h
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/* Megumin LED display firmware
|
||||
* Copyright (C) 2018 Sebastian Götte <code@jaseg.net>
|
||||
*
|
||||
* 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 __GLOBAL_H__
|
||||
#define __GLOBAL_H__
|
||||
|
||||
/* Workaround for sub-par ST libraries */
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
|
||||
#include <stm32f0xx.h>
|
||||
#include <stm32f0xx_ll_utils.h>
|
||||
#include <stm32f0xx_ll_spi.h>
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
#include <system_stm32f0xx.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Microcontroller part number: STM32F030F4P6 */
|
||||
|
||||
/* Things used for module status reporting. */
|
||||
#define FIRMWARE_VERSION 1
|
||||
#define HARDWARE_VERSION 0
|
||||
|
||||
#define TS_CAL1 (*(uint16_t *)0x1FFFF7B8)
|
||||
#define VREFINT_CAL (*(uint16_t *)0x1FFFF7BA)
|
||||
|
||||
#define ARRAY_LEN(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))
|
||||
|
||||
extern volatile unsigned int sys_time;
|
||||
extern volatile unsigned int sys_time_seconds;
|
||||
|
||||
#define UNUSED(var) ((void)var)
|
||||
|
||||
union leds {
|
||||
struct {
|
||||
unsigned int pps, sd_card, usb, ocxo, error, _nc1, _nc2, _nc3;
|
||||
};
|
||||
unsigned int arr[8];
|
||||
};
|
||||
|
||||
extern volatile union leds leds;
|
||||
extern volatile int32_t gps_1pps_period_sysclk;
|
||||
|
||||
#endif/*__GLOBAL_H__*/
|
||||
3699
hardware/fw/grid_scope.ipynb
Normal file
3699
hardware/fw/grid_scope.ipynb
Normal file
File diff suppressed because one or more lines are too long
241
hardware/fw/main.c
Normal file
241
hardware/fw/main.c
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
/* Megumin LED display firmware
|
||||
* Copyright (C) 2018 Sebastian Götte <code@jaseg.net>
|
||||
*
|
||||
* 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 "global.h"
|
||||
#include "adc.h"
|
||||
#include "serial.h"
|
||||
|
||||
|
||||
volatile unsigned int sys_time_seconds = 0;
|
||||
volatile union leds leds;
|
||||
volatile int32_t gps_1pps_period_sysclk = -1;
|
||||
|
||||
int main(void) {
|
||||
/* Get GPIOA and SPI1 up to flash status LEDs */
|
||||
RCC->AHBENR |= RCC_AHBENR_GPIOAEN;
|
||||
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
|
||||
|
||||
GPIOA->MODER |=
|
||||
(3<<GPIO_MODER_MODER2_Pos) /* PA2 - LINE_MEAS */
|
||||
| (1<<GPIO_MODER_MODER3_Pos) /* PA3 - LED_STB */
|
||||
| (1<<GPIO_MODER_MODER4_Pos) /* PA4 - SD_CS */
|
||||
| (2<<GPIO_MODER_MODER5_Pos) /* PA5 - SCK */
|
||||
| (2<<GPIO_MODER_MODER6_Pos) /* PA6 - MISO */
|
||||
| (2<<GPIO_MODER_MODER7_Pos) /* PA7 - MOSI */
|
||||
| (2<<GPIO_MODER_MODER9_Pos) /* PA9 - HOST_RX */
|
||||
| (2<<GPIO_MODER_MODER10_Pos);/* PA10 - HOST_TX */
|
||||
|
||||
/* Set shift register IO GPIO output speed */
|
||||
GPIOA->OSPEEDR |=
|
||||
(2<<GPIO_OSPEEDR_OSPEEDR3_Pos) /* LED_STB */
|
||||
| (2<<GPIO_OSPEEDR_OSPEEDR4_Pos) /* SD_CS */
|
||||
| (2<<GPIO_OSPEEDR_OSPEEDR5_Pos) /* SCK */
|
||||
| (2<<GPIO_OSPEEDR_OSPEEDR7_Pos) /* MOSI */
|
||||
| (2<<GPIO_OSPEEDR_OSPEEDR9_Pos); /* HOST_RX */
|
||||
|
||||
GPIOA->AFR[0] = (0<<GPIO_AFRL_AFRL5_Pos) | (0<<GPIO_AFRL_AFRL6_Pos) | (0<<GPIO_AFRL_AFRL7_Pos);
|
||||
GPIOA->AFR[1] = (1<<8) | (1<<4);
|
||||
|
||||
SPI1->CR1 =
|
||||
SPI_CR1_SSM
|
||||
| SPI_CR1_SSI
|
||||
| SPI_CR1_CPOL
|
||||
| SPI_CR1_CPHA
|
||||
| (4<<SPI_CR1_BR_Pos) /* /32 ~1.5MHz */
|
||||
| SPI_CR1_MSTR;
|
||||
SPI1->CR2 = (7<<SPI_CR2_DS_Pos);
|
||||
SPI1->CR1 |= SPI_CR1_SPE;
|
||||
*((volatile uint8_t*)&(SPI1->DR)) = 0xff;
|
||||
|
||||
/* Wait for OCXO to settle */
|
||||
for (int i=0; i<1000000; i++)
|
||||
;
|
||||
|
||||
/* Switch clock to PLL based on OCXO input */
|
||||
RCC->CR |= RCC_CR_HSEBYP;
|
||||
RCC->CR |= RCC_CR_HSEON;
|
||||
RCC->CFGR &= ~RCC_CFGR_PLLMUL_Msk & ~RCC_CFGR_SW_Msk & ~RCC_CFGR_PPRE_Msk & ~RCC_CFGR_HPRE_Msk;
|
||||
/* PLL config: 19.44MHz /2 x5 -> 48.6MHz */
|
||||
RCC->CFGR |= ((5-2)<<RCC_CFGR_PLLMUL_Pos) | RCC_CFGR_PLLSRC_HSE_PREDIV;
|
||||
RCC->CFGR2 = ((2-1)<<RCC_CFGR2_PREDIV_Pos);
|
||||
RCC->CR |= RCC_CR_PLLON;
|
||||
while (!(RCC->CR&RCC_CR_PLLRDY));
|
||||
RCC->CFGR |= (2<<RCC_CFGR_SW_Pos);
|
||||
SystemCoreClockUpdate();
|
||||
|
||||
/* Start systick */
|
||||
SysTick_Config(SystemCoreClock/10); /* 100ms interval */
|
||||
NVIC_EnableIRQ(SysTick_IRQn);
|
||||
NVIC_SetPriority(SysTick_IRQn, 3<<5);
|
||||
|
||||
/* Turn on rest of periphery */
|
||||
RCC->AHBENR |= RCC_AHBENR_DMAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_FLITFEN | RCC_AHBENR_CRCEN;
|
||||
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN | RCC_APB2ENR_ADCEN | RCC_APB2ENR_DBGMCUEN |\
|
||||
RCC_APB2ENR_TIM1EN | RCC_APB2ENR_TIM16EN | RCC_APB2ENR_USART1EN;
|
||||
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN | RCC_APB1ENR_TIM14EN;
|
||||
|
||||
GPIOB->MODER |=
|
||||
(2<<GPIO_MODER_MODER1_Pos); /* PB0 - GPS 1pps input */
|
||||
GPIOB->AFR[0] = (0<<GPIO_AFRL_AFRL1_Pos);
|
||||
GPIOB->PUPDR = 2<<GPIO_PUPDR_PUPDR1_Pos;
|
||||
|
||||
/* Configure TIM16 for LED update via SPI */
|
||||
TIM16->CR2 = 0;
|
||||
TIM16->DIER = TIM_DIER_UIE | TIM_DIER_CC1IE;
|
||||
TIM16->CCMR1 = 0;
|
||||
TIM16->CCR1 = 32;
|
||||
TIM16->PSC = 48-1; /* 1us */
|
||||
TIM16->ARR = 1000-1; /* 1ms */
|
||||
TIM16->CR1 = TIM_CR1_CEN;
|
||||
NVIC_EnableIRQ(TIM16_IRQn);
|
||||
|
||||
/* Configure TIM14 for GPS 1pps input capture */
|
||||
TIM14->CCMR1 = (1<<TIM_CCMR1_CC1S_Pos) | (3<<TIM_CCMR1_IC1F_Pos);
|
||||
TIM14->CCER = TIM_CCER_CC1E;
|
||||
TIM14->PSC = 1;
|
||||
TIM14->ARR = 0xffff;
|
||||
TIM14->DIER = TIM_DIER_CC1IE | TIM_DIER_UIE;
|
||||
TIM14->EGR = TIM_EGR_UG;
|
||||
TIM14->CR1 |= TIM_CR1_CEN;
|
||||
NVIC_EnableIRQ(TIM14_IRQn);
|
||||
|
||||
adc_init(1000000);
|
||||
adc_timer_init(243, 200); /* 19.44 MHz / 243 -> 200 kHz; /200 -> 1 kHz */
|
||||
|
||||
usart_dma_init();
|
||||
|
||||
while (42) {
|
||||
/* Do nothing and let the interrupts do all the work. */
|
||||
}
|
||||
}
|
||||
|
||||
void tim14_sr_cc1of(void) {} /* gdb hook */
|
||||
|
||||
void TIM14_IRQHandler(void) {
|
||||
static uint32_t gps_1pps_period = 0;
|
||||
static uint32_t update_inc = 0;
|
||||
static bool in_sync = false;
|
||||
|
||||
uint32_t sr = TIM14->SR;
|
||||
if (sr & TIM_SR_CC1OF) {
|
||||
TIM14->SR &= ~(TIM_SR_CC1IF | TIM_SR_CC1OF);
|
||||
tim14_sr_cc1of();
|
||||
|
||||
}
|
||||
if (sr & TIM_SR_UIF) {
|
||||
TIM14->SR &= ~TIM_SR_UIF;
|
||||
if (in_sync) {
|
||||
gps_1pps_period += update_inc;
|
||||
if (gps_1pps_period > 30000000) { /* Signal out of range */
|
||||
in_sync = false;
|
||||
gps_1pps_period_sysclk = -1;
|
||||
gps_1pps_period = (uint32_t)-1;
|
||||
}
|
||||
}
|
||||
update_inc = 0x10000;
|
||||
}
|
||||
|
||||
if (sr & TIM_SR_CC1IF) { /* CC1 event (GPS 1pps input) */
|
||||
/* Don't reset update event: If update event arrives while CC1 event is being processed leave UIF set to process
|
||||
* update event immediately after return from ISR. */
|
||||
uint16_t ccr = TIM14->CCR1;
|
||||
if (in_sync) {
|
||||
uint32_t new_period = gps_1pps_period + ccr;
|
||||
if (new_period < 20000000 || new_period > 30000000) { /* Signal out of range */
|
||||
in_sync = false;
|
||||
gps_1pps_period_sysclk = -1;
|
||||
gps_1pps_period = (uint32_t)-1;
|
||||
} else {
|
||||
if ((sr & TIM_SR_UIF) /* we processed an update event in this ISR */
|
||||
&& (ccr > 0xc000) /* and the capture happened late in the cycle */
|
||||
) {
|
||||
gps_1pps_period_sysclk = new_period - 0x10000;
|
||||
update_inc = 0x10000;
|
||||
gps_1pps_period = 0x10000 - ccr;
|
||||
} else {
|
||||
gps_1pps_period_sysclk = new_period;
|
||||
update_inc = 0x10000 - ccr; /* remaining cycles in this period */
|
||||
gps_1pps_period = 0;
|
||||
}
|
||||
leds.pps = 200; /* ms */
|
||||
}
|
||||
} else {
|
||||
gps_1pps_period = 0;
|
||||
update_inc = 0x10000 - ccr; /* remaining cycles in this period */
|
||||
in_sync = true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void TIM16_IRQHandler(void) {
|
||||
static int leds_update_counter = 0;
|
||||
if (TIM16->SR & TIM_SR_UIF) {
|
||||
TIM16->SR &= ~TIM_SR_UIF;
|
||||
|
||||
uint8_t bits = 0, mask = 1;
|
||||
for (int i=0; i<8; i++) {
|
||||
if (leds.arr[i]) {
|
||||
leds.arr[i]--;
|
||||
bits |= mask;
|
||||
}
|
||||
mask <<= 1;
|
||||
}
|
||||
|
||||
if (leds_update_counter++ == 10) {
|
||||
leds_update_counter = 0;
|
||||
|
||||
/* Workaround for SPI hardware bug: Even if configured to 8-bit mode, the SPI will do a 16-bit transfer if the
|
||||
* data register is accessed through a 16-bit write. Unfortunately, the STMCube register defs define DR as an
|
||||
* uint16_t, so we have to do some magic here to force an 8-bit write. */
|
||||
*((volatile uint8_t*)&(SPI1->DR)) = bits;
|
||||
GPIOA->BRR = 1<<3;
|
||||
}
|
||||
} else {
|
||||
TIM16->SR &= ~TIM_SR_CC1IF;
|
||||
GPIOA->BSRR = 1<<3;
|
||||
}
|
||||
}
|
||||
|
||||
void NMI_Handler(void) {
|
||||
asm volatile ("bkpt");
|
||||
}
|
||||
|
||||
void HardFault_Handler(void) __attribute__((naked));
|
||||
void HardFault_Handler() {
|
||||
asm volatile ("bkpt");
|
||||
}
|
||||
|
||||
void SVC_Handler(void) {
|
||||
asm volatile ("bkpt");
|
||||
}
|
||||
|
||||
|
||||
void PendSV_Handler(void) {
|
||||
asm volatile ("bkpt");
|
||||
}
|
||||
|
||||
void SysTick_Handler(void) {
|
||||
static int n = 0;
|
||||
if (n++ == 10) {
|
||||
n = 0;
|
||||
sys_time_seconds++;
|
||||
if (gps_1pps_period_sysclk < 0)
|
||||
leds.pps = 200; /* ms */
|
||||
}
|
||||
}
|
||||
|
||||
162
hardware/fw/main.c.bak
Normal file
162
hardware/fw/main.c.bak
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
/* Megumin LED display firmware
|
||||
* Copyright (C) 2018 Sebastian Götte <code@jaseg.net>
|
||||
*
|
||||
* 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 "global.h"
|
||||
|
||||
#include "adc.h"
|
||||
|
||||
volatile unsigned int sys_time = 0;
|
||||
volatile unsigned int sys_time_seconds = 0;
|
||||
|
||||
void TIM1_BRK_UP_TRG_COM_Handler() {
|
||||
TIM1->SR &= ~TIM_SR_UIF_Msk;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
RCC->CR |= RCC_CR_HSEON;
|
||||
while (!(RCC->CR&RCC_CR_HSERDY));
|
||||
RCC->CFGR &= ~RCC_CFGR_PLLMUL_Msk & ~RCC_CFGR_SW_Msk & ~RCC_CFGR_PPRE_Msk & ~RCC_CFGR_HPRE_Msk;
|
||||
RCC->CFGR |= ((6-2)<<RCC_CFGR_PLLMUL_Pos) | RCC_CFGR_PLLSRC_HSE_PREDIV; /* PLL x6 -> 48.0MHz */
|
||||
RCC->CR |= RCC_CR_PLLON;
|
||||
while (!(RCC->CR&RCC_CR_PLLRDY));
|
||||
RCC->CFGR |= (2<<RCC_CFGR_SW_Pos);
|
||||
SystemCoreClockUpdate();
|
||||
SysTick_Config(SystemCoreClock/1000); /* 1ms interval */
|
||||
|
||||
/* Turn on lots of neat things */
|
||||
RCC->AHBENR |= RCC_AHBENR_DMAEN | RCC_AHBENR_GPIOAEN | RCC_AHBENR_FLITFEN;
|
||||
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN | RCC_APB2ENR_ADCEN| RCC_APB2ENR_DBGMCUEN | RCC_APB2ENR_TIM1EN | RCC_APB2ENR_TIM1EN;;
|
||||
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
|
||||
|
||||
GPIOA->MODER |=
|
||||
(3<<GPIO_MODER_MODER0_Pos) /* PA0 - Vmeas_A to ADC */
|
||||
| (3<<GPIO_MODER_MODER1_Pos) /* PA1 - Vmeas_B to ADC */
|
||||
| (1<<GPIO_MODER_MODER2_Pos) /* PA2 - LOAD */
|
||||
| (1<<GPIO_MODER_MODER3_Pos) /* PA3 - CH0 */
|
||||
| (1<<GPIO_MODER_MODER4_Pos) /* PA4 - CH3 */
|
||||
| (0<<GPIO_MODER_MODER5_Pos) /* PA5 - TP1 */
|
||||
| (1<<GPIO_MODER_MODER6_Pos) /* PA6 - CH2 */
|
||||
| (1<<GPIO_MODER_MODER7_Pos) /* PA7 - CH1 */
|
||||
| (0<<GPIO_MODER_MODER9_Pos) /* PA9 - TP2 */
|
||||
| (0<<GPIO_MODER_MODER10_Pos);/* PA10 - TP3 */
|
||||
|
||||
/* Set shift register IO GPIO output speed */
|
||||
GPIOA->OSPEEDR |=
|
||||
(2<<GPIO_OSPEEDR_OSPEEDR2_Pos) /* LOAD */
|
||||
| (2<<GPIO_OSPEEDR_OSPEEDR3_Pos) /* CH0 */
|
||||
| (2<<GPIO_OSPEEDR_OSPEEDR4_Pos) /* CH3 */
|
||||
| (2<<GPIO_OSPEEDR_OSPEEDR6_Pos) /* CH2 */
|
||||
| (2<<GPIO_OSPEEDR_OSPEEDR7_Pos); /* CH1 */
|
||||
|
||||
/* Setup CC1 and CC2. CC2 generates the LED drivers' STROBE, CC1 triggers the IRQ handler */
|
||||
TIM1->BDTR = TIM_BDTR_MOE;
|
||||
TIM1->CCMR2 = (6<<TIM_CCMR2_OC4M_Pos); /* PWM Mode 1 */
|
||||
TIM1->CCER = TIM_CCER_CC4E;
|
||||
TIM1->CCR4 = 1;
|
||||
TIM1->DIER = TIM_DIER_UIE;
|
||||
|
||||
TIM1->PSC = SystemCoreClock/500000 - 1; /* 0.5us/tick */
|
||||
TIM1->ARR = 25-1;
|
||||
/* Preload all values */
|
||||
TIM1->EGR |= TIM_EGR_UG;
|
||||
TIM1->CR1 = TIM_CR1_ARPE;
|
||||
/* And... go! */
|
||||
TIM1->CR1 |= TIM_CR1_CEN;
|
||||
|
||||
void set_outputs(uint8_t val) {
|
||||
int a=!!(val&1), b=!!(val&2), c=!!(val&4), d=!!(val&8);
|
||||
GPIOA->ODR &= ~(!a<<3 | !b<<7 | c<<6 | d<<4);
|
||||
GPIOA->ODR |= a<<3 | b<<7 | !c<<6 | !d<<4;
|
||||
}
|
||||
set_outputs(0);
|
||||
|
||||
adc_init();
|
||||
|
||||
uint8_t out_state = 0x01;
|
||||
#define DEBOUNCE 100
|
||||
int debounce_ctr = 0;
|
||||
int val_last = 0;
|
||||
int ctr = 0;
|
||||
#define RESET 1000
|
||||
int reset_ctr = 0;
|
||||
while (42) {
|
||||
#define FOO 500000
|
||||
if (reset_ctr)
|
||||
reset_ctr--;
|
||||
else
|
||||
set_outputs(0);
|
||||
|
||||
if (debounce_ctr) {
|
||||
debounce_ctr--;
|
||||
} else {
|
||||
int val = !!(GPIOA->IDR & 1);
|
||||
debounce_ctr = DEBOUNCE;
|
||||
|
||||
if (val != val_last) {
|
||||
if (val)
|
||||
set_outputs(out_state & 0xf);
|
||||
else
|
||||
set_outputs(out_state >> 4);
|
||||
reset_ctr = RESET;
|
||||
val_last = val;
|
||||
ctr++;
|
||||
|
||||
if (ctr == 100) {
|
||||
ctr = 0;
|
||||
out_state = out_state<<1 | out_state>>7;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
for (int i=0; i<FOO; i++) ;
|
||||
set_outputs(0x1);
|
||||
for (int i=0; i<FOO; i++) ;
|
||||
set_outputs(0x2);
|
||||
for (int i=0; i<FOO; i++) ;
|
||||
set_outputs(0x4);
|
||||
for (int i=0; i<FOO; i++) ;
|
||||
set_outputs(0x8);
|
||||
*/
|
||||
//for (int i=0; i<8*FOO; i++) ;
|
||||
//GPIOA->ODR ^= 4;
|
||||
}
|
||||
}
|
||||
|
||||
void NMI_Handler(void) {
|
||||
}
|
||||
|
||||
void HardFault_Handler(void) __attribute__((naked));
|
||||
void HardFault_Handler() {
|
||||
asm volatile ("bkpt");
|
||||
}
|
||||
|
||||
void SVC_Handler(void) {
|
||||
}
|
||||
|
||||
|
||||
void PendSV_Handler(void) {
|
||||
}
|
||||
|
||||
void SysTick_Handler(void) {
|
||||
static int n = 0;
|
||||
sys_time++;
|
||||
if (n++ == 1000) {
|
||||
n = 0;
|
||||
sys_time_seconds++;
|
||||
}
|
||||
}
|
||||
|
||||
17
hardware/fw/openocd.cfg
Normal file
17
hardware/fw/openocd.cfg
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
telnet_port 4445
|
||||
gdb_port 3334
|
||||
tcl_port 6667
|
||||
|
||||
source [find interface/stlink-v2.cfg]
|
||||
#interface jlink
|
||||
#interface stlink-v2
|
||||
#adapter_khz 10000
|
||||
#transport select swd
|
||||
|
||||
#source /usr/share/openocd/scripts/target/stm32f0x.cfg
|
||||
source [find target/stm32f0x_stlink.cfg]
|
||||
|
||||
init
|
||||
arm semihosting enable
|
||||
|
||||
#flash bank sysflash.alias stm32f0x 0x00000000 0 0 0 $_TARGETNAME
|
||||
46
hardware/fw/packet_interface.c
Normal file
46
hardware/fw/packet_interface.c
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
|
||||
#include "packet_interface.h"
|
||||
#include "cobs.h"
|
||||
|
||||
void usart2_isr(void) {
|
||||
TRACING_SET(TR_HOST_IF_USART_IRQ);
|
||||
static struct cobs_decode_state host_cobs_state = {0};
|
||||
if (USART2_SR & USART_SR_ORE) { /* Overrun handling */
|
||||
LOG_PRINTF("USART2 data register overrun\n");
|
||||
/* Clear interrupt flag */
|
||||
(void)USART2_DR; /* FIXME make sure this read is not optimized out */
|
||||
host_packet_length = -1;
|
||||
TRACING_CLEAR(TR_HOST_IF_USART_IRQ);
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t data = USART2_DR; /* This automatically acknowledges the IRQ */
|
||||
|
||||
if (host_packet_length) {
|
||||
LOG_PRINTF("USART2 COBS buffer overrun\n");
|
||||
host_packet_length = -1;
|
||||
TRACING_CLEAR(TR_HOST_IF_USART_IRQ);
|
||||
return;
|
||||
}
|
||||
|
||||
ssize_t rv = cobs_decode_incremental(&host_cobs_state, (char *)host_packet_buf, sizeof(host_packet_buf), data);
|
||||
if (rv == -2) {
|
||||
LOG_PRINTF("Host interface COBS packet too large\n");
|
||||
host_packet_length = -1;
|
||||
} else if (rv == -3) {
|
||||
LOG_PRINTF("Got double null byte from host\n");
|
||||
} else if (rv < 0) {
|
||||
LOG_PRINTF("Host interface COBS framing error\n");
|
||||
host_packet_length = -1;
|
||||
} else if (rv > 0) {
|
||||
host_packet_length = rv;
|
||||
} /* else just return and wait for next byte */
|
||||
TRACING_CLEAR(TR_HOST_IF_USART_IRQ);
|
||||
}
|
||||
|
||||
void send_packet(struct dma_usart_file *f, const uint8_t *data, size_t len) {
|
||||
/* ignore return value as putf is blocking and always succeeds */
|
||||
(void)cobs_encode_incremental(f, putf, (char *)data, len);
|
||||
flush(f);
|
||||
}
|
||||
|
||||
6
hardware/fw/packet_interface.h
Normal file
6
hardware/fw/packet_interface.h
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
#ifndef __PACKET_INTERFACE_H__
|
||||
#define __PACKET_INTERFACE_H__
|
||||
|
||||
void send_packet(struct dma_usart_file *f, const uint8_t *data, size_t len);
|
||||
|
||||
#endif
|
||||
30
hardware/fw/reader.py
Normal file
30
hardware/fw/reader.py
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import struct
|
||||
|
||||
import sqlite3
|
||||
|
||||
import serial
|
||||
from cobs import cobs
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-b', '--baudrate', type=int, default=250000)
|
||||
parser.add_argument('port')
|
||||
parser.add_argument('dbfile')
|
||||
args = parser.parse_args()
|
||||
|
||||
db = sqlite3.connect(args.db)
|
||||
ser = serial.Serial(args.port, args.baudrate)
|
||||
|
||||
while True:
|
||||
packet = ser.read_until(b'\0')
|
||||
try:
|
||||
packet = cobs.decode(packet)
|
||||
crc, seq, struct.decode('IBxH', packet[:8])
|
||||
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
12
hardware/fw/scope.gdb
Normal file
12
hardware/fw/scope.gdb
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
target remote 192.168.178.103:3334
|
||||
set pagination off
|
||||
file main.elf
|
||||
load
|
||||
|
||||
break gdb_dump
|
||||
command 1
|
||||
dump binary value /tmp/scope_dump.bin adc_buf
|
||||
continue
|
||||
end
|
||||
|
||||
continue
|
||||
251
hardware/fw/serial.c
Normal file
251
hardware/fw/serial.c
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
#include "global.h"
|
||||
#include "serial.h"
|
||||
#include "cobs.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
volatile struct dma_tx_buf usart_tx_buf;
|
||||
|
||||
static uint32_t tx_overruns=0, rx_overruns=0;
|
||||
static uint32_t rx_framing_errors=0, rx_protocol_errors=0;
|
||||
|
||||
static struct cobs_decode_state cobs_state;
|
||||
|
||||
static volatile uint8_t rx_buf[32];
|
||||
|
||||
|
||||
static void usart_schedule_dma(void);
|
||||
static int usart_putc_nonblocking(uint8_t c);
|
||||
|
||||
|
||||
void usart_dma_reset() {
|
||||
usart_tx_buf.xfr_start = -1;
|
||||
usart_tx_buf.xfr_end = 0;
|
||||
usart_tx_buf.wr_pos = 0;
|
||||
usart_tx_buf.wr_idx = 0;
|
||||
usart_tx_buf.xfr_next = 0;
|
||||
usart_tx_buf.wraparound = false;
|
||||
usart_tx_buf.ack = true;
|
||||
|
||||
for (size_t i=0; i<ARRAY_LEN(usart_tx_buf.packet_end); i++)
|
||||
usart_tx_buf.packet_end[i] = -1;
|
||||
|
||||
cobs_decode_incremental_initialize(&cobs_state);
|
||||
}
|
||||
|
||||
void usart_dma_init() {
|
||||
usart_dma_reset();
|
||||
|
||||
/* Configure DMA 1 Channel 2 to handle uart transmission */
|
||||
DMA1_Channel2->CPAR = (uint32_t)&(USART1->TDR);
|
||||
DMA1_Channel2->CCR = (0<<DMA_CCR_PL_Pos)
|
||||
| DMA_CCR_DIR
|
||||
| (0<<DMA_CCR_MSIZE_Pos) /* 8 bit */
|
||||
| (0<<DMA_CCR_PSIZE_Pos) /* 8 bit */
|
||||
| DMA_CCR_MINC
|
||||
| DMA_CCR_TCIE; /* Enable transfer complete interrupt. */
|
||||
|
||||
DMA1_Channel3->CMAR = (uint32_t)&(CRC->DR);
|
||||
DMA1_Channel3->CCR = (1<<DMA_CCR_PL_Pos)
|
||||
| (0<<DMA_CCR_MSIZE_Pos) /* 8 bit */
|
||||
| (0<<DMA_CCR_PSIZE_Pos) /* 8 bit */
|
||||
| DMA_CCR_PINC
|
||||
| DMA_CCR_TCIE; /* Enable transfer complete interrupt. */
|
||||
|
||||
/* triggered on transfer completion. We use this to process the ADC data */
|
||||
NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);
|
||||
NVIC_SetPriority(DMA1_Channel2_3_IRQn, 2<<5);
|
||||
|
||||
USART1->CR1 = /* 8-bit -> M1, M0 clear */
|
||||
/* OVER8 clear. Use default 16x oversampling */
|
||||
/* CMIF clear */
|
||||
USART_CR1_MME
|
||||
/* WAKE clear */
|
||||
/* PCE, PS clear */
|
||||
| USART_CR1_RXNEIE /* Enable receive interrupt */
|
||||
/* other interrupts clear */
|
||||
| USART_CR1_TE
|
||||
| USART_CR1_RE;
|
||||
/* Set divider for 115.2kBd @48MHz system clock. */
|
||||
//USART1->BRR = 417;
|
||||
|
||||
//USART1->BRR = 48; /* 1MBd */
|
||||
//USART1->BRR = 96; /* 500kBd */
|
||||
USART1->BRR = 192; /* 250kBd */
|
||||
//USART1->BRR = 208; /* 230400 */
|
||||
|
||||
USART1->CR2 = USART_CR2_TXINV | USART_CR2_RXINV;
|
||||
|
||||
USART1->CR3 |= USART_CR3_DMAT; /* TX DMA enable */
|
||||
|
||||
/* Enable receive interrupt */
|
||||
NVIC_EnableIRQ(USART1_IRQn);
|
||||
NVIC_SetPriority(USART1_IRQn, 1<<5);
|
||||
|
||||
/* And... go! */
|
||||
USART1->CR1 |= USART_CR1_UE;
|
||||
}
|
||||
|
||||
void USART1_IRQHandler() {
|
||||
uint32_t isr = USART1->ISR;
|
||||
|
||||
if (isr & USART_ISR_ORE) {
|
||||
USART1->ICR = USART_ICR_ORECF;
|
||||
rx_overruns++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (isr & USART_ISR_RXNE) {
|
||||
uint8_t c = USART1->RDR;
|
||||
|
||||
int rc = cobs_decode_incremental(&cobs_state, (char *)rx_buf, sizeof(rx_buf), c);
|
||||
if (rc == 0) /* packet still incomplete */
|
||||
return;
|
||||
|
||||
if (rc < 0) {
|
||||
rx_framing_errors++;
|
||||
return;
|
||||
}
|
||||
|
||||
/* A complete frame received */
|
||||
if (rc != 2) {
|
||||
rx_protocol_errors++;
|
||||
return;
|
||||
}
|
||||
|
||||
volatile struct ctrl_pkt *pkt = (volatile struct ctrl_pkt *)rx_buf;
|
||||
|
||||
switch (pkt->type) {
|
||||
case CTRL_PKT_RESET:
|
||||
usart_dma_reset();
|
||||
break;
|
||||
|
||||
case CTRL_PKT_ACK:
|
||||
usart_tx_buf.ack = true;
|
||||
if (!(DMA1_Channel2->CCR & DMA_CCR_EN))
|
||||
usart_schedule_dma();
|
||||
break;
|
||||
|
||||
default:
|
||||
rx_protocol_errors++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void usart_schedule_dma() {
|
||||
volatile struct dma_tx_buf *buf = &usart_tx_buf;
|
||||
|
||||
ssize_t xfr_start, xfr_end, xfr_len;
|
||||
if (buf->wraparound) {
|
||||
buf->wraparound = false;
|
||||
xfr_start = 0;
|
||||
xfr_len = buf->xfr_end;
|
||||
xfr_end = buf->xfr_end;
|
||||
|
||||
} else if (buf->ack) {
|
||||
if (buf->packet_end[buf->xfr_next] == -1)
|
||||
return; /* Nothing to trasnmit */
|
||||
|
||||
buf->ack = false;
|
||||
|
||||
xfr_start = buf->xfr_end;
|
||||
xfr_end = buf->packet_end[buf->xfr_next];
|
||||
buf->packet_end[buf->xfr_next] = -1;
|
||||
buf->xfr_next = (buf->xfr_next + 1) % ARRAY_LEN(buf->packet_end);
|
||||
|
||||
if (xfr_end > xfr_start) { /* no wraparound */
|
||||
xfr_len = xfr_end - xfr_start;
|
||||
|
||||
} else { /* wraparound */
|
||||
if (xfr_end != 0)
|
||||
buf->wraparound = true;
|
||||
xfr_len = sizeof(buf->data) - xfr_start;
|
||||
}
|
||||
|
||||
} else {
|
||||
/* retransmit */
|
||||
/* First, send a zero to delimit any garbage from the following good packet */
|
||||
USART1->TDR = 0x00;
|
||||
|
||||
xfr_start = buf->xfr_start;
|
||||
xfr_end = buf->xfr_end;
|
||||
|
||||
if (xfr_end > xfr_start) { /* no wraparound */
|
||||
xfr_len = xfr_end - xfr_start;
|
||||
|
||||
} else { /* wraparound */
|
||||
if (xfr_end != 0)
|
||||
buf->wraparound = true;
|
||||
xfr_len = sizeof(buf->data) - xfr_start;
|
||||
}
|
||||
|
||||
leds.error = 250;
|
||||
}
|
||||
|
||||
buf->xfr_start = xfr_start;
|
||||
buf->xfr_end = xfr_end;
|
||||
|
||||
/* initiate transmission of new buffer */
|
||||
DMA1_Channel2->CMAR = (uint32_t)(buf->data + xfr_start);
|
||||
DMA1_Channel2->CNDTR = xfr_len;
|
||||
DMA1_Channel2->CCR |= DMA_CCR_EN;
|
||||
}
|
||||
|
||||
int usart_putc_nonblocking(uint8_t c) {
|
||||
volatile struct dma_tx_buf *buf = &usart_tx_buf;
|
||||
|
||||
if (buf->wr_pos == buf->xfr_start)
|
||||
return -EBUSY;
|
||||
|
||||
buf->data[buf->wr_pos] = c;
|
||||
buf->wr_pos = (buf->wr_pos + 1) % sizeof(buf->data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void DMA1_Channel2_3_IRQHandler(void) {
|
||||
/* Transfer complete */
|
||||
DMA1->IFCR |= DMA_IFCR_CTCIF2;
|
||||
|
||||
DMA1_Channel2->CCR &= ~DMA_CCR_EN;
|
||||
if (usart_tx_buf.wraparound)
|
||||
usart_schedule_dma();
|
||||
}
|
||||
|
||||
/* len is the packet length including headers */
|
||||
int usart_send_packet_nonblocking(struct ll_pkt *pkt, size_t pkt_len) {
|
||||
|
||||
if (usart_tx_buf.packet_end[usart_tx_buf.wr_idx] != -1) {
|
||||
/* Find a free slot for this packet */
|
||||
tx_overruns++;
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
pkt->pid = usart_tx_buf.wr_idx;
|
||||
pkt->_pad = usart_tx_buf.xfr_next;
|
||||
|
||||
/* make the value this wonky-ass CRC implementation produces match zlib etc. */
|
||||
CRC->CR = CRC_CR_REV_OUT | (1<<CRC_CR_REV_IN_Pos) | CRC_CR_RESET;
|
||||
for (size_t i=offsetof(struct ll_pkt, pid); i<pkt_len; i++)
|
||||
CRC->DR = ((uint8_t *)pkt)[i];
|
||||
|
||||
pkt->crc32 = ~CRC->DR;
|
||||
|
||||
int rc = cobs_encode_usart((int (*)(char))usart_putc_nonblocking, (char *)pkt, pkt_len);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
usart_tx_buf.packet_end[usart_tx_buf.wr_idx] = usart_tx_buf.wr_pos;
|
||||
usart_tx_buf.wr_idx = (usart_tx_buf.wr_idx + 1) % ARRAY_LEN(usart_tx_buf.packet_end);
|
||||
|
||||
leds.usb = 100;
|
||||
|
||||
if (!(DMA1_Channel2->CCR & DMA_CCR_EN))
|
||||
usart_schedule_dma();
|
||||
return 0;
|
||||
}
|
||||
|
||||
75
hardware/fw/serial.h
Normal file
75
hardware/fw/serial.h
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* This file is part of the libusbhost library
|
||||
* hosted at http://github.com/libusbhost/libusbhost
|
||||
*
|
||||
* Copyright (C) 2015 Amir Hammad <amir.hammad@hotmail.com>
|
||||
*
|
||||
*
|
||||
* libusbhost is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This library 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __SERIAL_H__
|
||||
#define __SERIAL_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "global.h"
|
||||
|
||||
struct dma_tx_buf {
|
||||
/* The following fields are accessed only from DMA ISR */
|
||||
ssize_t xfr_start; /* Start index of running DMA transfer */
|
||||
ssize_t xfr_end; /* End index of running DMA transfer plus one */
|
||||
bool wraparound;
|
||||
ssize_t xfr_next;
|
||||
bool ack;
|
||||
|
||||
|
||||
/* The following fields are written only from non-interrupt code */
|
||||
ssize_t wr_pos; /* Next index to be written */
|
||||
ssize_t wr_idx;
|
||||
ssize_t packet_end[8];
|
||||
|
||||
/* The following may be accessed by anything */
|
||||
uint8_t data[512];
|
||||
};
|
||||
|
||||
struct __attribute__((__packed__)) ll_pkt {
|
||||
uint32_t crc32;
|
||||
/* CRC computed over entire packet starting here */
|
||||
uint8_t pid;
|
||||
uint8_t _pad;
|
||||
uint8_t data[];
|
||||
};
|
||||
|
||||
enum ctrl_pkt_type {
|
||||
CTRL_PKT_RESET = 1,
|
||||
CTRL_PKT_ACK = 2,
|
||||
};
|
||||
|
||||
struct __attribute__((__packed__)) ctrl_pkt {
|
||||
uint8_t type;
|
||||
uint8_t orig_id;
|
||||
};
|
||||
|
||||
extern volatile struct dma_tx_buf usart_tx_buf;
|
||||
|
||||
void usart_dma_init(void);
|
||||
int usart_send_packet_nonblocking(struct ll_pkt *pkt, size_t pkt_len);
|
||||
int usart_ack_packet(uint8_t idx);
|
||||
|
||||
#endif // __SERIAL_H__
|
||||
273
hardware/fw/startup_stm32f030x6.s
Normal file
273
hardware/fw/startup_stm32f030x6.s
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* @file startup_stm32f030x6.s
|
||||
* copied from: STM32Cube/Drivers/CMSIS/Device/ST/STM32F0xx/Source/Templates/gcc
|
||||
* @author MCD Application Team
|
||||
* @version V2.3.1
|
||||
* @date 04-November-2016
|
||||
* @brief STM32F030x4/STM32F030x6 devices vector table for Atollic TrueSTUDIO toolchain.
|
||||
* This module performs:
|
||||
* - Set the initial SP
|
||||
* - Set the initial PC == Reset_Handler,
|
||||
* - Set the vector table entries with the exceptions ISR address
|
||||
* - Branches to main in the C library (which eventually
|
||||
* calls main()).
|
||||
* After Reset the Cortex-M0 processor is in Thread mode,
|
||||
* priority is Privileged, and the Stack is set to Main.
|
||||
******************************************************************************
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of STMicroelectronics nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
.syntax unified
|
||||
.cpu cortex-m0
|
||||
.fpu softvfp
|
||||
.thumb
|
||||
|
||||
.global g_pfnVectors
|
||||
.global Default_Handler
|
||||
|
||||
/* start address for the initialization values of the .data section.
|
||||
defined in linker script */
|
||||
.word _sidata
|
||||
/* start address for the .data section. defined in linker script */
|
||||
.word _sdata
|
||||
/* end address for the .data section. defined in linker script */
|
||||
.word _edata
|
||||
/* start address for the .bss section. defined in linker script */
|
||||
.word _sbss
|
||||
/* end address for the .bss section. defined in linker script */
|
||||
.word _ebss
|
||||
|
||||
.section .text.Reset_Handler
|
||||
.weak Reset_Handler
|
||||
.type Reset_Handler, %function
|
||||
Reset_Handler:
|
||||
ldr r0, =_estack
|
||||
mov sp, r0 /* set stack pointer */
|
||||
|
||||
/* Copy the data segment initializers from flash to SRAM */
|
||||
movs r1, #0
|
||||
b LoopCopyDataInit
|
||||
|
||||
CopyDataInit:
|
||||
ldr r3, =_sidata
|
||||
ldr r3, [r3, r1]
|
||||
str r3, [r0, r1]
|
||||
adds r1, r1, #4
|
||||
|
||||
LoopCopyDataInit:
|
||||
ldr r0, =_sdata
|
||||
ldr r3, =_edata
|
||||
adds r2, r0, r1
|
||||
cmp r2, r3
|
||||
bcc CopyDataInit
|
||||
ldr r2, =_sbss
|
||||
b LoopFillZerobss
|
||||
/* Zero fill the bss segment. */
|
||||
FillZerobss:
|
||||
movs r3, #0
|
||||
str r3, [r2]
|
||||
adds r2, r2, #4
|
||||
|
||||
|
||||
LoopFillZerobss:
|
||||
ldr r3, = _ebss
|
||||
cmp r2, r3
|
||||
bcc FillZerobss
|
||||
|
||||
/* Call the clock system intitialization function.*/
|
||||
bl SystemInit
|
||||
/* Call static constructors */
|
||||
// bl __libc_init_array
|
||||
/* Call the application's entry point.*/
|
||||
bl main
|
||||
|
||||
LoopForever:
|
||||
b LoopForever
|
||||
|
||||
|
||||
.size Reset_Handler, .-Reset_Handler
|
||||
|
||||
/**
|
||||
* @brief This is the code that gets called when the processor receives an
|
||||
* unexpected interrupt. This simply enters an infinite loop, preserving
|
||||
* the system state for examination by a debugger.
|
||||
*
|
||||
* @param None
|
||||
* @retval : None
|
||||
*/
|
||||
.section .text.Default_Handler,"ax",%progbits
|
||||
Default_Handler:
|
||||
Infinite_Loop:
|
||||
b Infinite_Loop
|
||||
.size Default_Handler, .-Default_Handler
|
||||
/******************************************************************************
|
||||
*
|
||||
* The minimal vector table for a Cortex M0. Note that the proper constructs
|
||||
* must be placed on this to ensure that it ends up at physical address
|
||||
* 0x0000.0000.
|
||||
*
|
||||
******************************************************************************/
|
||||
.section .isr_vector,"a",%progbits
|
||||
.type g_pfnVectors, %object
|
||||
.size g_pfnVectors, .-g_pfnVectors
|
||||
|
||||
|
||||
g_pfnVectors:
|
||||
.word _estack
|
||||
.word Reset_Handler
|
||||
.word NMI_Handler
|
||||
.word HardFault_Handler
|
||||
.word 0
|
||||
.word 0
|
||||
.word 0
|
||||
.word 0
|
||||
.word 0
|
||||
.word 0
|
||||
.word 0
|
||||
.word SVC_Handler
|
||||
.word 0
|
||||
.word 0
|
||||
.word PendSV_Handler
|
||||
.word SysTick_Handler
|
||||
.word WWDG_IRQHandler /* Window WatchDog */
|
||||
.word 0 /* Reserved */
|
||||
.word RTC_IRQHandler /* RTC through the EXTI line */
|
||||
.word FLASH_IRQHandler /* FLASH */
|
||||
.word RCC_IRQHandler /* RCC */
|
||||
.word EXTI0_1_IRQHandler /* EXTI Line 0 and 1 */
|
||||
.word EXTI2_3_IRQHandler /* EXTI Line 2 and 3 */
|
||||
.word EXTI4_15_IRQHandler /* EXTI Line 4 to 15 */
|
||||
.word 0 /* Reserved */
|
||||
.word DMA1_Channel1_IRQHandler /* DMA1 Channel 1 */
|
||||
.word DMA1_Channel2_3_IRQHandler /* DMA1 Channel 2 and Channel 3 */
|
||||
.word DMA1_Channel4_5_IRQHandler /* DMA1 Channel 4 and Channel 5 */
|
||||
.word ADC1_IRQHandler /* ADC1 */
|
||||
.word TIM1_BRK_UP_TRG_COM_IRQHandler /* TIM1 Break, Update, Trigger and Commutation */
|
||||
.word TIM1_CC_IRQHandler /* TIM1 Capture Compare */
|
||||
.word 0 /* Reserved */
|
||||
.word TIM3_IRQHandler /* TIM3 */
|
||||
.word 0 /* Reserved */
|
||||
.word 0 /* Reserved */
|
||||
.word TIM14_IRQHandler /* TIM14 */
|
||||
.word 0 /* Reserved */
|
||||
.word TIM16_IRQHandler /* TIM16 */
|
||||
.word TIM17_IRQHandler /* TIM17 */
|
||||
.word I2C1_IRQHandler /* I2C1 */
|
||||
.word 0 /* Reserved */
|
||||
.word SPI1_IRQHandler /* SPI1 */
|
||||
.word 0 /* Reserved */
|
||||
.word USART1_IRQHandler /* USART1 */
|
||||
.word 0 /* Reserved */
|
||||
.word 0 /* Reserved */
|
||||
.word 0 /* Reserved */
|
||||
.word 0 /* Reserved */
|
||||
|
||||
/*******************************************************************************
|
||||
*
|
||||
* Provide weak aliases for each Exception handler to the Default_Handler.
|
||||
* As they are weak aliases, any function with the same name will override
|
||||
* this definition.
|
||||
*
|
||||
*******************************************************************************/
|
||||
|
||||
.weak NMI_Handler
|
||||
.thumb_set NMI_Handler,Default_Handler
|
||||
|
||||
.weak HardFault_Handler
|
||||
.thumb_set HardFault_Handler,Default_Handler
|
||||
|
||||
.weak SVC_Handler
|
||||
.thumb_set SVC_Handler,Default_Handler
|
||||
|
||||
.weak PendSV_Handler
|
||||
.thumb_set PendSV_Handler,Default_Handler
|
||||
|
||||
.weak SysTick_Handler
|
||||
.thumb_set SysTick_Handler,Default_Handler
|
||||
|
||||
.weak WWDG_IRQHandler
|
||||
.thumb_set WWDG_IRQHandler,Default_Handler
|
||||
|
||||
.weak RTC_IRQHandler
|
||||
.thumb_set RTC_IRQHandler,Default_Handler
|
||||
|
||||
.weak FLASH_IRQHandler
|
||||
.thumb_set FLASH_IRQHandler,Default_Handler
|
||||
|
||||
.weak RCC_IRQHandler
|
||||
.thumb_set RCC_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI0_1_IRQHandler
|
||||
.thumb_set EXTI0_1_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI2_3_IRQHandler
|
||||
.thumb_set EXTI2_3_IRQHandler,Default_Handler
|
||||
|
||||
.weak EXTI4_15_IRQHandler
|
||||
.thumb_set EXTI4_15_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA1_Channel1_IRQHandler
|
||||
.thumb_set DMA1_Channel1_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA1_Channel2_3_IRQHandler
|
||||
.thumb_set DMA1_Channel2_3_IRQHandler,Default_Handler
|
||||
|
||||
.weak DMA1_Channel4_5_IRQHandler
|
||||
.thumb_set DMA1_Channel4_5_IRQHandler,Default_Handler
|
||||
|
||||
.weak ADC1_IRQHandler
|
||||
.thumb_set ADC1_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM1_BRK_UP_TRG_COM_IRQHandler
|
||||
.thumb_set TIM1_BRK_UP_TRG_COM_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM1_CC_IRQHandler
|
||||
.thumb_set TIM1_CC_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM3_IRQHandler
|
||||
.thumb_set TIM3_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM14_IRQHandler
|
||||
.thumb_set TIM14_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM16_IRQHandler
|
||||
.thumb_set TIM16_IRQHandler,Default_Handler
|
||||
|
||||
.weak TIM17_IRQHandler
|
||||
.thumb_set TIM17_IRQHandler,Default_Handler
|
||||
|
||||
.weak I2C1_IRQHandler
|
||||
.thumb_set I2C1_IRQHandler,Default_Handler
|
||||
|
||||
.weak SPI1_IRQHandler
|
||||
.thumb_set SPI1_IRQHandler,Default_Handler
|
||||
|
||||
.weak USART1_IRQHandler
|
||||
.thumb_set USART1_IRQHandler,Default_Handler
|
||||
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
||||
|
||||
136
hardware/fw/stm32_flash.ld
Normal file
136
hardware/fw/stm32_flash.ld
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
|
||||
ENTRY(Reset_Handler)
|
||||
|
||||
MEMORY {
|
||||
FLASH (rx): ORIGIN = 0x08000000, LENGTH = 0x3C00
|
||||
CONFIGFLASH (rw): ORIGIN = 0x08003C00, LENGTH = 0x400
|
||||
RAM (xrw): ORIGIN = 0x20000000, LENGTH = 4K
|
||||
}
|
||||
|
||||
/* highest address of the user mode stack */
|
||||
_estack = 0x20001000;
|
||||
|
||||
SECTIONS {
|
||||
/* for Cortex devices, the beginning of the startup code is stored in the .isr_vector section, which goes to FLASH */
|
||||
.isr_vector : {
|
||||
. = ALIGN(4);
|
||||
KEEP(*(.isr_vector)) /* Startup code */
|
||||
. = ALIGN(4);
|
||||
} >FLASH
|
||||
|
||||
/* the program code is stored in the .text section, which goes to Flash */
|
||||
.text : {
|
||||
. = ALIGN(4);
|
||||
|
||||
*(.text) /* normal code */
|
||||
*(.text.*) /* -ffunction-sections code */
|
||||
*(.rodata) /* read-only data (constants) */
|
||||
*(.rodata*) /* -fdata-sections read only data */
|
||||
*(.glue_7) /* TBD - needed ? */
|
||||
*(.glue_7t) /* TBD - needed ? */
|
||||
|
||||
*(.source_tarball)
|
||||
|
||||
/* Necessary KEEP sections (see http://sourceware.org/ml/newlib/2005/msg00255.html) */
|
||||
KEEP (*(.init))
|
||||
KEEP (*(.fini))
|
||||
KEEP (*(.source_tarball))
|
||||
|
||||
. = ALIGN(4);
|
||||
_etext = .;
|
||||
/* This is used by the startup in order to initialize the .data section */
|
||||
_sidata = _etext;
|
||||
} >FLASH
|
||||
|
||||
/*
|
||||
.configflash : {
|
||||
. = ALIGN(0x400);
|
||||
*(.configdata)
|
||||
_econfig = .;
|
||||
} >FLASH
|
||||
*/
|
||||
|
||||
/* This is the initialized data section
|
||||
The program executes knowing that the data is in the RAM
|
||||
but the loader puts the initial values in the FLASH (inidata).
|
||||
It is one task of the startup to copy the initial values from FLASH to RAM. */
|
||||
.data : AT ( _sidata ) {
|
||||
. = ALIGN(4);
|
||||
/* This is used by the startup in order to initialize the .data secion */
|
||||
_sdata = . ;
|
||||
_data = . ;
|
||||
|
||||
*(.data)
|
||||
*(.data.*)
|
||||
*(.RAMtext)
|
||||
|
||||
. = ALIGN(4);
|
||||
/* This is used by the startup in order to initialize the .data secion */
|
||||
_edata = . ;
|
||||
} >RAM
|
||||
|
||||
/* This is the uninitialized data section */
|
||||
.bss : {
|
||||
. = ALIGN(4);
|
||||
/* This is used by the startup in order to initialize the .bss secion */
|
||||
_sbss = .;
|
||||
_bss = .;
|
||||
|
||||
*(.bss)
|
||||
*(.bss.*) /* patched by elias - allows the use of -fdata-sections */
|
||||
*(COMMON)
|
||||
|
||||
. = ALIGN(4);
|
||||
/* This is used by the startup in order to initialize the .bss secion */
|
||||
_ebss = . ;
|
||||
} >RAM
|
||||
|
||||
PROVIDE ( end = _ebss);
|
||||
PROVIDE (_end = _ebss);
|
||||
|
||||
__exidx_start = .;
|
||||
__exidx_end = .;
|
||||
|
||||
/* after that it's only debugging information. */
|
||||
|
||||
/* remove the debugging information from the standard libraries */
|
||||
/* /DISCARD/ : {
|
||||
libc.a ( * )
|
||||
libm.a ( * )
|
||||
libgcc.a ( * )
|
||||
}*/
|
||||
|
||||
/* Stabs debugging sections. */
|
||||
.stab 0 : { *(.stab) }
|
||||
.stabstr 0 : { *(.stabstr) }
|
||||
.stab.excl 0 : { *(.stab.excl) }
|
||||
.stab.exclstr 0 : { *(.stab.exclstr) }
|
||||
.stab.index 0 : { *(.stab.index) }
|
||||
.stab.indexstr 0 : { *(.stab.indexstr) }
|
||||
.comment 0 : { *(.comment) }
|
||||
/* DWARF debug sections.
|
||||
Symbols in the DWARF debugging sections are relative to the beginning
|
||||
of the section so we begin them at 0. */
|
||||
/* DWARF 1 */
|
||||
.debug 0 : { *(.debug) }
|
||||
.line 0 : { *(.line) }
|
||||
/* GNU DWARF 1 extensions */
|
||||
.debug_srcinfo 0 : { *(.debug_srcinfo) }
|
||||
.debug_sfnames 0 : { *(.debug_sfnames) }
|
||||
/* DWARF 1.1 and DWARF 2 */
|
||||
.debug_aranges 0 : { *(.debug_aranges) }
|
||||
.debug_pubnames 0 : { *(.debug_pubnames) }
|
||||
/* DWARF 2 */
|
||||
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
|
||||
.debug_abbrev 0 : { *(.debug_abbrev) }
|
||||
.debug_line 0 : { *(.debug_line) }
|
||||
.debug_frame 0 : { *(.debug_frame) }
|
||||
.debug_str 0 : { *(.debug_str) }
|
||||
.debug_loc 0 : { *(.debug_loc) }
|
||||
.debug_macinfo 0 : { *(.debug_macinfo) }
|
||||
/* SGI/MIPS DWARF 2 extensions */
|
||||
.debug_weaknames 0 : { *(.debug_weaknames) }
|
||||
.debug_funcnames 0 : { *(.debug_funcnames) }
|
||||
.debug_typenames 0 : { *(.debug_typenames) }
|
||||
.debug_varnames 0 : { *(.debug_varnames) }
|
||||
}
|
||||
336
hardware/fw/system_stm32f0xx.c
Normal file
336
hardware/fw/system_stm32f0xx.c
Normal file
|
|
@ -0,0 +1,336 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* @file system_stm32f0xx.c
|
||||
* copied from: STM32Cube/Drivers/CMSIS/Device/ST/STM32F0xx/Source/Templates
|
||||
* @author MCD Application Team
|
||||
* @version V2.3.1
|
||||
* @date 04-November-2016
|
||||
* @brief CMSIS Cortex-M0 Device Peripheral Access Layer System Source File.
|
||||
*
|
||||
* 1. This file provides two functions and one global variable to be called from
|
||||
* user application:
|
||||
* - SystemInit(): This function is called at startup just after reset and
|
||||
* before branch to main program. This call is made inside
|
||||
* the "startup_stm32f0xx.s" file.
|
||||
*
|
||||
* - SystemCoreClock variable: Contains the core clock (HCLK), it can be used
|
||||
* by the user application to setup the SysTick
|
||||
* timer or configure other parameters.
|
||||
*
|
||||
* - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must
|
||||
* be called whenever the core clock is changed
|
||||
* during program execution.
|
||||
*
|
||||
* 2. After each device reset the HSI (8 MHz) is used as system clock source.
|
||||
* Then SystemInit() function is called, in "startup_stm32f0xx.s" file, to
|
||||
* configure the system clock before to branch to main program.
|
||||
*
|
||||
* 3. This file configures the system clock as follows:
|
||||
*=============================================================================
|
||||
* Supported STM32F0xx device
|
||||
*-----------------------------------------------------------------------------
|
||||
* System Clock source | HSI
|
||||
*-----------------------------------------------------------------------------
|
||||
* SYSCLK(Hz) | 8000000
|
||||
*-----------------------------------------------------------------------------
|
||||
* HCLK(Hz) | 8000000
|
||||
*-----------------------------------------------------------------------------
|
||||
* AHB Prescaler | 1
|
||||
*-----------------------------------------------------------------------------
|
||||
* APB1 Prescaler | 1
|
||||
*-----------------------------------------------------------------------------
|
||||
*=============================================================================
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* <h2><center>© COPYRIGHT(c) 2016 STMicroelectronics</center></h2>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of STMicroelectronics nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/** @addtogroup CMSIS
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @addtogroup stm32f0xx_system
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @addtogroup STM32F0xx_System_Private_Includes
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include "stm32f0xx.h"
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @addtogroup STM32F0xx_System_Private_TypesDefinitions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @addtogroup STM32F0xx_System_Private_Defines
|
||||
* @{
|
||||
*/
|
||||
#if !defined (HSE_VALUE)
|
||||
#define HSE_VALUE ((uint32_t)8000000) /*!< Default value of the External oscillator in Hz.
|
||||
This value can be provided and adapted by the user application. */
|
||||
#endif /* HSE_VALUE */
|
||||
|
||||
#if !defined (HSI_VALUE)
|
||||
#define HSI_VALUE ((uint32_t)8000000) /*!< Default value of the Internal oscillator in Hz.
|
||||
This value can be provided and adapted by the user application. */
|
||||
#endif /* HSI_VALUE */
|
||||
|
||||
#if !defined (HSI48_VALUE)
|
||||
#define HSI48_VALUE ((uint32_t)48000000) /*!< Default value of the HSI48 Internal oscillator in Hz.
|
||||
This value can be provided and adapted by the user application. */
|
||||
#endif /* HSI48_VALUE */
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @addtogroup STM32F0xx_System_Private_Macros
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @addtogroup STM32F0xx_System_Private_Variables
|
||||
* @{
|
||||
*/
|
||||
/* This variable is updated in three ways:
|
||||
1) by calling CMSIS function SystemCoreClockUpdate()
|
||||
2) by calling HAL API function HAL_RCC_GetHCLKFreq()
|
||||
3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency
|
||||
Note: If you use this function to configure the system clock there is no need to
|
||||
call the 2 first functions listed above, since SystemCoreClock variable is
|
||||
updated automatically.
|
||||
*/
|
||||
uint32_t SystemCoreClock = 8000000;
|
||||
|
||||
const uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
|
||||
const uint8_t APBPrescTable[8] = {0, 0, 0, 0, 1, 2, 3, 4};
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @addtogroup STM32F0xx_System_Private_FunctionPrototypes
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/** @addtogroup STM32F0xx_System_Private_Functions
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Setup the microcontroller system.
|
||||
* Initialize the default HSI clock source, vector table location and the PLL configuration is reset.
|
||||
* @param None
|
||||
* @retval None
|
||||
*/
|
||||
void SystemInit(void)
|
||||
{
|
||||
/* Reset the RCC clock configuration to the default reset state ------------*/
|
||||
/* Set HSION bit */
|
||||
RCC->CR |= (uint32_t)0x00000001U;
|
||||
|
||||
#if defined (STM32F051x8) || defined (STM32F058x8)
|
||||
/* Reset SW[1:0], HPRE[3:0], PPRE[2:0], ADCPRE and MCOSEL[2:0] bits */
|
||||
RCC->CFGR &= (uint32_t)0xF8FFB80CU;
|
||||
#else
|
||||
/* Reset SW[1:0], HPRE[3:0], PPRE[2:0], ADCPRE, MCOSEL[2:0], MCOPRE[2:0] and PLLNODIV bits */
|
||||
RCC->CFGR &= (uint32_t)0x08FFB80CU;
|
||||
#endif /* STM32F051x8 or STM32F058x8 */
|
||||
|
||||
/* Reset HSEON, CSSON and PLLON bits */
|
||||
RCC->CR &= (uint32_t)0xFEF6FFFFU;
|
||||
|
||||
/* Reset HSEBYP bit */
|
||||
RCC->CR &= (uint32_t)0xFFFBFFFFU;
|
||||
|
||||
/* Reset PLLSRC, PLLXTPRE and PLLMUL[3:0] bits */
|
||||
RCC->CFGR &= (uint32_t)0xFFC0FFFFU;
|
||||
|
||||
/* Reset PREDIV[3:0] bits */
|
||||
RCC->CFGR2 &= (uint32_t)0xFFFFFFF0U;
|
||||
|
||||
#if defined (STM32F072xB) || defined (STM32F078xx)
|
||||
/* Reset USART2SW[1:0], USART1SW[1:0], I2C1SW, CECSW, USBSW and ADCSW bits */
|
||||
RCC->CFGR3 &= (uint32_t)0xFFFCFE2CU;
|
||||
#elif defined (STM32F071xB)
|
||||
/* Reset USART2SW[1:0], USART1SW[1:0], I2C1SW, CECSW and ADCSW bits */
|
||||
RCC->CFGR3 &= (uint32_t)0xFFFFCEACU;
|
||||
#elif defined (STM32F091xC) || defined (STM32F098xx)
|
||||
/* Reset USART3SW[1:0], USART2SW[1:0], USART1SW[1:0], I2C1SW, CECSW and ADCSW bits */
|
||||
RCC->CFGR3 &= (uint32_t)0xFFF0FEACU;
|
||||
#elif defined (STM32F030x6) || defined (STM32F030x8) || defined (STM32F031x6) || defined (STM32F038xx) || defined (STM32F030xC)
|
||||
/* Reset USART1SW[1:0], I2C1SW and ADCSW bits */
|
||||
RCC->CFGR3 &= (uint32_t)0xFFFFFEECU;
|
||||
#elif defined (STM32F051x8) || defined (STM32F058xx)
|
||||
/* Reset USART1SW[1:0], I2C1SW, CECSW and ADCSW bits */
|
||||
RCC->CFGR3 &= (uint32_t)0xFFFFFEACU;
|
||||
#elif defined (STM32F042x6) || defined (STM32F048xx)
|
||||
/* Reset USART1SW[1:0], I2C1SW, CECSW, USBSW and ADCSW bits */
|
||||
RCC->CFGR3 &= (uint32_t)0xFFFFFE2CU;
|
||||
#elif defined (STM32F070x6) || defined (STM32F070xB)
|
||||
/* Reset USART1SW[1:0], I2C1SW, USBSW and ADCSW bits */
|
||||
RCC->CFGR3 &= (uint32_t)0xFFFFFE6CU;
|
||||
/* Set default USB clock to PLLCLK, since there is no HSI48 */
|
||||
RCC->CFGR3 |= (uint32_t)0x00000080U;
|
||||
#else
|
||||
#warning "No target selected"
|
||||
#endif
|
||||
|
||||
/* Reset HSI14 bit */
|
||||
RCC->CR2 &= (uint32_t)0xFFFFFFFEU;
|
||||
|
||||
/* Disable all interrupts */
|
||||
RCC->CIR = 0x00000000U;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update SystemCoreClock variable according to Clock Register Values.
|
||||
* The SystemCoreClock variable contains the core clock (HCLK), it can
|
||||
* be used by the user application to setup the SysTick timer or configure
|
||||
* other parameters.
|
||||
*
|
||||
* @note Each time the core clock (HCLK) changes, this function must be called
|
||||
* to update SystemCoreClock variable value. Otherwise, any configuration
|
||||
* based on this variable will be incorrect.
|
||||
*
|
||||
* @note - The system frequency computed by this function is not the real
|
||||
* frequency in the chip. It is calculated based on the predefined
|
||||
* constant and the selected clock source:
|
||||
*
|
||||
* - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*)
|
||||
*
|
||||
* - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**)
|
||||
*
|
||||
* - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**)
|
||||
* or HSI_VALUE(*) multiplied/divided by the PLL factors.
|
||||
*
|
||||
* (*) HSI_VALUE is a constant defined in stm32f0xx_hal.h file (default value
|
||||
* 8 MHz) but the real value may vary depending on the variations
|
||||
* in voltage and temperature.
|
||||
*
|
||||
* (**) HSE_VALUE is a constant defined in stm32f0xx_hal.h file (default value
|
||||
* 8 MHz), user has to ensure that HSE_VALUE is same as the real
|
||||
* frequency of the crystal used. Otherwise, this function may
|
||||
* have wrong result.
|
||||
*
|
||||
* - The result of this function could be not correct when using fractional
|
||||
* value for HSE crystal.
|
||||
*
|
||||
* @param None
|
||||
* @retval None
|
||||
*/
|
||||
void SystemCoreClockUpdate (void)
|
||||
{
|
||||
uint32_t tmp = 0, pllmull = 0, pllsource = 0, predivfactor = 0;
|
||||
|
||||
/* Get SYSCLK source -------------------------------------------------------*/
|
||||
tmp = RCC->CFGR & RCC_CFGR_SWS;
|
||||
|
||||
switch (tmp)
|
||||
{
|
||||
case RCC_CFGR_SWS_HSI: /* HSI used as system clock */
|
||||
SystemCoreClock = HSI_VALUE;
|
||||
break;
|
||||
case RCC_CFGR_SWS_HSE: /* HSE used as system clock */
|
||||
SystemCoreClock = HSE_VALUE;
|
||||
break;
|
||||
case RCC_CFGR_SWS_PLL: /* PLL used as system clock */
|
||||
/* Get PLL clock source and multiplication factor ----------------------*/
|
||||
pllmull = RCC->CFGR & RCC_CFGR_PLLMUL;
|
||||
pllsource = RCC->CFGR & RCC_CFGR_PLLSRC;
|
||||
pllmull = ( pllmull >> 18) + 2;
|
||||
predivfactor = (RCC->CFGR2 & RCC_CFGR2_PREDIV) + 1;
|
||||
|
||||
if (pllsource == RCC_CFGR_PLLSRC_HSE_PREDIV)
|
||||
{
|
||||
/* HSE used as PLL clock source : SystemCoreClock = HSE/PREDIV * PLLMUL */
|
||||
SystemCoreClock = (HSE_VALUE/predivfactor) * pllmull;
|
||||
}
|
||||
#if defined(STM32F042x6) || defined(STM32F048xx) || defined(STM32F072xB) || defined(STM32F078xx) || defined(STM32F091xC) || defined(STM32F098xx)
|
||||
else if (pllsource == RCC_CFGR_PLLSRC_HSI48_PREDIV)
|
||||
{
|
||||
/* HSI48 used as PLL clock source : SystemCoreClock = HSI48/PREDIV * PLLMUL */
|
||||
SystemCoreClock = (HSI48_VALUE/predivfactor) * pllmull;
|
||||
}
|
||||
#endif /* STM32F042x6 || STM32F048xx || STM32F072xB || STM32F078xx || STM32F091xC || STM32F098xx */
|
||||
else
|
||||
{
|
||||
#if defined(STM32F042x6) || defined(STM32F048xx) || defined(STM32F070x6) \
|
||||
|| defined(STM32F078xx) || defined(STM32F071xB) || defined(STM32F072xB) \
|
||||
|| defined(STM32F070xB) || defined(STM32F091xC) || defined(STM32F098xx) || defined(STM32F030xC)
|
||||
/* HSI used as PLL clock source : SystemCoreClock = HSI/PREDIV * PLLMUL */
|
||||
SystemCoreClock = (HSI_VALUE/predivfactor) * pllmull;
|
||||
#else
|
||||
/* HSI used as PLL clock source : SystemCoreClock = HSI/2 * PLLMUL */
|
||||
SystemCoreClock = (HSI_VALUE >> 1) * pllmull;
|
||||
#endif /* STM32F042x6 || STM32F048xx || STM32F070x6 ||
|
||||
STM32F071xB || STM32F072xB || STM32F078xx || STM32F070xB ||
|
||||
STM32F091xC || STM32F098xx || STM32F030xC */
|
||||
}
|
||||
break;
|
||||
default: /* HSI used as system clock */
|
||||
SystemCoreClock = HSI_VALUE;
|
||||
break;
|
||||
}
|
||||
/* Compute HCLK clock frequency ----------------*/
|
||||
/* Get HCLK prescaler */
|
||||
tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)];
|
||||
/* HCLK clock frequency */
|
||||
SystemCoreClock >>= tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|
||||
|
||||
41
hardware/fw/test.py
Normal file
41
hardware/fw/test.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import serial
|
||||
import time
|
||||
|
||||
#ser = serial.Serial('/dev/serial/by-id/usb-1a86_USB2.0-Serial-if00-port0', 230400)
|
||||
ser = serial.Serial('/dev/serial/by-id/usb-Silicon_Labs_CP2102_USB_to_UART_Bridge_Controller_0001-if00-port0', 250000)
|
||||
#while True:
|
||||
# ser.write(bytes(range(256)))
|
||||
start = time.time()
|
||||
|
||||
last_val = None
|
||||
run = 0
|
||||
total_errors = 0
|
||||
rx_bytes = 0
|
||||
last_print = time.time()
|
||||
while True:
|
||||
bytes = ser.read(256)
|
||||
for byte in bytes:
|
||||
if last_val is not None and byte != (last_val + 1) % 256:
|
||||
if run > 0:
|
||||
print(f'{time.time()-start:>8.3f} {run} {last_val:02x} {byte:02x}')
|
||||
run = 0
|
||||
total_errors += 1
|
||||
else:
|
||||
run += 1
|
||||
rx_bytes += 1
|
||||
|
||||
if time.time() - last_print > 5:
|
||||
last_print = time.time()
|
||||
print(f'{time.time()-start:>8.3f} {run} [all good] err={total_errors}@rx={rx_bytes}B',
|
||||
f'(rate 1/{rx_bytes/total_errors:.5g})' if total_errors > 0 else 'rate unknown')
|
||||
last_val = byte
|
||||
|
||||
#while True:
|
||||
# data = ser.read_until(b'\0')
|
||||
# print(f'{time.time()-start:>8.3f} {len(data)}')
|
||||
|
||||
# while True:
|
||||
# data = ser.read(256)
|
||||
# print('YES' if b'\0' in data else 'NO ', data)
|
||||
30
hardware/fw/tools/gen_cmsis_exports.py
Normal file
30
hardware/fw/tools/gen_cmsis_exports.py
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#!/usr/bin/env python3
|
||||
import re
|
||||
import os
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('cmsis_device_header', nargs='+', type=argparse.FileType('rb'))
|
||||
args = parser.parse_args()
|
||||
|
||||
print('#ifndef __GENERATED_CMSIS_HEADER_EXPORTS__')
|
||||
print('#define __GENERATED_CMSIS_HEADER_EXPORTS__')
|
||||
print()
|
||||
for header in args.cmsis_device_header:
|
||||
lines = header.readlines()
|
||||
name = os.path.basename(header.name)
|
||||
print('#include <{}>'.format(name))
|
||||
print()
|
||||
|
||||
print('/* {} */'.format(name))
|
||||
for l in lines:
|
||||
match = re.match(b'^#define (\w+)\s+\W*(\w+_TypeDef|\w+_Type).*$', l)
|
||||
if match:
|
||||
inst, typedef = match.groups()
|
||||
inst, typedef = inst.decode(), typedef.decode()
|
||||
print('{} *{} = {};'.format(typedef, inst.lower(), inst))
|
||||
print()
|
||||
print('#endif//__GENERATED_CMSIS_HEADER_EXPORTS__')
|
||||
|
||||
450
hardware/fw/tw_test.c
Normal file
450
hardware/fw/tw_test.c
Normal file
|
|
@ -0,0 +1,450 @@
|
|||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <asm/termbits.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdint.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/types.h>
|
||||
#include <assert.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <sqlite3.h>
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
int set_interface_attribs (int fd, int baudrate) {
|
||||
struct termios2 tio;
|
||||
memset (&tio, 0, sizeof(tio));
|
||||
if (ioctl (fd, TCGETS2, &tio) != 0) {
|
||||
fprintf(stderr, "Could not request termios for given port\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* FIXME set baudrate */
|
||||
|
||||
tio.c_cflag = (tio.c_cflag & ~CSIZE) | CS8; /* 8 bit */
|
||||
/* disable IGNBRK for mismatched speed tests; otherwise receive break as \000 chars */
|
||||
tio.c_iflag &= ~IGNBRK; /* disable break processing */
|
||||
tio.c_lflag = 0; /* no signaling chars, no echo, no canonical processing */
|
||||
tio.c_oflag = 0; /* no remapping, no delays */
|
||||
|
||||
tio.c_iflag &= ~(IXON | IXOFF | IXANY); /* shut off xon/xoff ctrl */
|
||||
|
||||
tio.c_cflag |= (CLOCAL | CREAD);/* ignore modem controls, enable reading */
|
||||
tio.c_cflag &= ~(PARENB | PARODD); /* no parity */
|
||||
tio.c_cflag &= ~CSTOPB;
|
||||
tio.c_cflag &= ~CRTSCTS;
|
||||
|
||||
tio.c_cflag &= ~(CBAUD | CBAUDEX);
|
||||
tio.c_cflag |= BOTHER;
|
||||
tio.c_ospeed = baudrate;
|
||||
tio.c_cflag &= ~((CBAUD | CBAUDEX) << IBSHIFT);
|
||||
tio.c_cflag |= (B0 << IBSHIFT); /* same as output baudrate */
|
||||
|
||||
tio.c_cc[VMIN] = 0; /* non-blocking mode */
|
||||
tio.c_cc[VTIME] = 10; /* 1000ms seconds read timeout */
|
||||
|
||||
if (ioctl (fd, TCSETS2, &tio)) {
|
||||
fprintf(stderr, "Could not set serial port attributes: Error %d in tcsetattr (\"%s\")\n", errno, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ssize_t cobs_decode(char *dst, size_t dstlen, char *src, size_t srclen) {
|
||||
size_t p = 1;
|
||||
size_t c = (unsigned char)src[0];
|
||||
if (c == 0)
|
||||
return -5; /* invalid framing. An empty frame would be [...] 00 01 00, not [...] 00 00 */
|
||||
|
||||
while (p < srclen && src[p]) {
|
||||
char val;
|
||||
c--;
|
||||
|
||||
if (c == 0) {
|
||||
c = (unsigned char)src[p];
|
||||
val = 0;
|
||||
} else {
|
||||
val = src[p];
|
||||
}
|
||||
|
||||
if (p > dstlen)
|
||||
return -4; /* Destination buffer too small */
|
||||
dst[p-1] = val;
|
||||
p++;
|
||||
}
|
||||
|
||||
if (p == srclen)
|
||||
return -2; /* Invalid framing. The terminating null byte should always be present in the input buffer. */
|
||||
|
||||
if (c != 1)
|
||||
return -3; /* Invalid framing. The skip counter does not hit the end of the frame. */
|
||||
|
||||
return p-1;
|
||||
}
|
||||
|
||||
int cobs_encode(char *dst, char *src, size_t srclen) {
|
||||
if (srclen > 254)
|
||||
return -1;
|
||||
|
||||
size_t p = 0;
|
||||
while (p <= srclen) {
|
||||
|
||||
char val;
|
||||
if (p != 0 && src[p-1] != 0) {
|
||||
val = src[p-1];
|
||||
|
||||
} else {
|
||||
size_t q = p;
|
||||
while (q < srclen && src[q] != 0)
|
||||
q++;
|
||||
val = (char)q-p+1;
|
||||
}
|
||||
|
||||
|
||||
*dst++ = val;
|
||||
p++;
|
||||
}
|
||||
|
||||
*dst++ = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void print_usage(char *prog) {
|
||||
fprintf(stderr, "Usage: %s [-p /dev/serial/some_port] [-b baudrate] dbfile.sqilte3\n", prog);
|
||||
}
|
||||
|
||||
void hexdump(const void* data, size_t size) {
|
||||
char ascii[17];
|
||||
size_t i, j;
|
||||
ascii[16] = '\0';
|
||||
for (i = 0; i < size; ++i) {
|
||||
printf("%02X ", ((unsigned char*)data)[i]);
|
||||
if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') {
|
||||
ascii[i % 16] = ((unsigned char*)data)[i];
|
||||
} else {
|
||||
ascii[i % 16] = '.';
|
||||
}
|
||||
if ((i+1) % 8 == 0 || i+1 == size) {
|
||||
printf(" ");
|
||||
if ((i+1) % 16 == 0) {
|
||||
printf("| %s \n", ascii);
|
||||
} else if (i+1 == size) {
|
||||
ascii[(i+1) % 16] = '\0';
|
||||
if ((i+1) % 16 <= 8) {
|
||||
printf(" ");
|
||||
}
|
||||
for (j = (i+1) % 16; j < 16; ++j) {
|
||||
printf(" ");
|
||||
}
|
||||
printf("| %s \n", ascii);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
int opt;
|
||||
int baudrate = 250000;
|
||||
char *endptr = NULL;
|
||||
char *port = NULL;
|
||||
char *dbfile = NULL;
|
||||
while ((opt = getopt(argc, argv, "p:b:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'p':
|
||||
port = optarg;
|
||||
break;
|
||||
case 'b':
|
||||
baudrate = strtol(optarg, &endptr, 10);
|
||||
if (errno == ERANGE || endptr == NULL || *endptr != '\0') {
|
||||
fprintf(stderr, "Invalid baudrate \"%s\"\n", optarg);
|
||||
print_usage(argv[0]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
print_usage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if (port == NULL) {
|
||||
DIR *le_dir = opendir("/dev/serial/by-id");
|
||||
if (le_dir == NULL) {
|
||||
fprintf(stderr, "No serial port given and could not find any in /dev/serial\n");
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
}
|
||||
|
||||
struct dirent *de;
|
||||
while ((de = readdir(le_dir))) {
|
||||
if (de == NULL) {
|
||||
fprintf(stderr, "No serial port given and could not find any in /dev/serial\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!strncmp(de->d_name, ".", sizeof(de->d_name)) ||
|
||||
!strncmp(de->d_name, "..", sizeof(de->d_name)))
|
||||
continue;
|
||||
|
||||
if (port != NULL) {
|
||||
fprintf(stderr, "No serial port given and found multiple candidates in /dev/serial\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
const char *prefix = "/dev/serial/by-id/";
|
||||
port = malloc(strlen(prefix) + sizeof(de->d_name) + 1);
|
||||
if (port == NULL) {
|
||||
fprintf(stderr, "Could not allocate memory\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
strcpy(port, prefix);
|
||||
strncat(port, de->d_name, sizeof(de->d_name));
|
||||
}
|
||||
fprintf(stderr, "No port given, defaulting to %s\n", port);
|
||||
closedir(le_dir);
|
||||
}
|
||||
|
||||
if (optind != argc - 1) {
|
||||
fprintf(stderr, "Too few arguments\n");
|
||||
print_usage(argv[0]);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
dbfile = argv[optind];
|
||||
printf("Using database file %s\n", dbfile);
|
||||
fflush(stdout);
|
||||
|
||||
int fd = open(port, O_RDWR|O_NOCTTY|O_SYNC);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Cannot open serial port: %s\n", strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (set_interface_attribs (fd, baudrate))
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
sqlite3 *db;
|
||||
if (sqlite3_open(dbfile, &db) != SQLITE_OK) {
|
||||
fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
|
||||
sqlite3_close(db);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
char *errmsg;
|
||||
if (sqlite3_exec(db,
|
||||
"CREATE TABLE IF NOT EXISTS measurements (rx_time INTEGER, tx_seq INTEGER, rx_seq INTEGER, data BLOB);",
|
||||
NULL, NULL, &errmsg) != SQLITE_OK) {
|
||||
fprintf(stderr, "Error initializing databse: %s\n", errmsg);
|
||||
sqlite3_close(db);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
const char *insert_sql = "INSERT INTO measurements VALUES (?, ?, ?, ?)";
|
||||
sqlite3_stmt *insert_stmt;
|
||||
if (sqlite3_prepare_v2(db, insert_sql, strlen(insert_sql), &insert_stmt, NULL) != SQLITE_OK) {
|
||||
fprintf(stderr, "Error compiling SQL: %s\n", sqlite3_errmsg(db));
|
||||
sqlite3_close(db);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
char buf [1024];
|
||||
int in_sync = 0, wpos = 0;
|
||||
struct __attribute__((__packed__)) {
|
||||
uint32_t crc;
|
||||
uint8_t pid;
|
||||
uint8_t _pad;
|
||||
uint16_t seq;
|
||||
uint16_t data[32];
|
||||
} packet;
|
||||
struct __attribute__((__packed__)) {
|
||||
uint8_t type;
|
||||
uint8_t pid;
|
||||
} wpacket;
|
||||
char wbuf[4];
|
||||
|
||||
int epollfd = epoll_create1(0);
|
||||
if (epollfd < 0)
|
||||
goto epoll_err;
|
||||
|
||||
#define MAX_EVENTS 10
|
||||
struct epoll_event ev, events[MAX_EVENTS];
|
||||
ev.events = EPOLLIN;
|
||||
ev.data.fd = fd;
|
||||
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) < 0)
|
||||
goto epoll_err;
|
||||
|
||||
wpacket.type = 1;
|
||||
wpacket.pid = 0;
|
||||
cobs_encode(wbuf, (char *)&wpacket, sizeof(wpacket));
|
||||
write(fd, wbuf, sizeof(wbuf));
|
||||
|
||||
/* FIXME begin debug code */
|
||||
for (int i=0; i<32; i++) {
|
||||
wpacket.type = 2;
|
||||
wpacket.pid = packet.pid;
|
||||
cobs_encode(wbuf, (char *)&wpacket, sizeof(wpacket));
|
||||
write(fd, wbuf, sizeof(wbuf));
|
||||
usleep(20);
|
||||
}
|
||||
/* FIXME end debug code */
|
||||
|
||||
int current_seq = -1;
|
||||
uint64_t local_seq = 0;
|
||||
while (23) {
|
||||
int nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
|
||||
if (nfds == -1)
|
||||
goto epoll_err;
|
||||
|
||||
if (nfds == 0)
|
||||
continue;
|
||||
|
||||
ssize_t n = read(fd, buf+wpos, sizeof(buf)-wpos);
|
||||
printf("--- read wpos=%d n=%ld\n", wpos, n);
|
||||
hexdump(buf+wpos, n);
|
||||
if (n<0) {
|
||||
if (errno == EAGAIN || errno == EINTR)
|
||||
continue;
|
||||
|
||||
fprintf(stderr, "Error reading from port: %s\n", strerror(errno));
|
||||
goto loop_err;
|
||||
}
|
||||
//printf("--- debug: read n=%d bytes at wpos=%d\n", n, wpos);
|
||||
//fflush(stdout);
|
||||
wpos += n;
|
||||
|
||||
while (23) {
|
||||
void *first_nul = memchr(buf, 0, wpos) ;
|
||||
ssize_t first_nul_offx = first_nul - (void*)buf;
|
||||
ssize_t remaining = wpos - first_nul_offx;
|
||||
|
||||
if (!in_sync) {
|
||||
if (first_nul) {
|
||||
memmove(buf, first_nul+1, remaining-1);
|
||||
wpos = remaining-1;
|
||||
in_sync = 1;
|
||||
continue;
|
||||
|
||||
} else {
|
||||
wpos = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!first_nul)
|
||||
break;
|
||||
|
||||
printf("--- debug: first_nul=%p (idx=%ld) wpos=%d remaining=%ld\n", first_nul, first_nul_offx, wpos, remaining);
|
||||
hexdump(buf, 80);
|
||||
|
||||
int rc = cobs_decode((char *)&packet, sizeof(packet), buf, wpos);
|
||||
if (rc < 0) {
|
||||
printf("Framing error: rc=%d\n", rc);
|
||||
goto it_err;
|
||||
}
|
||||
|
||||
/* Use zlib to calculate CRC32. The STM32 code calculates the CRC byte-wise, so we emulate this here. */
|
||||
uint32_t our_crc = 0;
|
||||
if (rc > 0) {
|
||||
uint8_t buf[4] = {0};
|
||||
for (int i=4; i<rc; i++) {
|
||||
buf[3] = ((uint8_t *)&packet)[i];
|
||||
our_crc = crc32(our_crc, buf, sizeof(buf));
|
||||
}
|
||||
}
|
||||
|
||||
bool error = false;
|
||||
/* Check CRC */
|
||||
if (our_crc != packet.crc) {
|
||||
printf("CRC mismatch: seq=%d packet=%08x computed=%08x\n", packet.pid, packet.crc, our_crc);
|
||||
error = true;
|
||||
}
|
||||
|
||||
/* Check device sequence number */
|
||||
int last_seq = current_seq;
|
||||
int predicted_seq = (last_seq+1) % 0xffff;
|
||||
if (!error)
|
||||
current_seq = packet.seq;
|
||||
if (last_seq >= 0 && packet.seq != predicted_seq) {
|
||||
printf("SEQ mismatch: packet=%d computed=%d\n", packet.seq, predicted_seq);
|
||||
error = true;
|
||||
}
|
||||
|
||||
if (error)
|
||||
goto it_err;
|
||||
|
||||
/* Write to database */
|
||||
struct timespec ts;
|
||||
if (clock_gettime(CLOCK_REALTIME, &ts)) {
|
||||
fprintf(stderr, "Error getting current wall-clock time: %s\n", strerror(errno));
|
||||
goto loop_err;
|
||||
}
|
||||
uint64_t timestamp = ts.tv_sec*1000 + ts.tv_nsec/1000000;
|
||||
|
||||
if (sqlite3_bind_int(insert_stmt, 1, timestamp) != SQLITE_OK)
|
||||
goto write_err;
|
||||
|
||||
if (sqlite3_bind_int(insert_stmt, 2, packet.seq) != SQLITE_OK)
|
||||
goto write_err;
|
||||
|
||||
if (sqlite3_bind_int(insert_stmt, 3, local_seq) != SQLITE_OK)
|
||||
goto write_err;
|
||||
|
||||
if (sqlite3_bind_blob(insert_stmt, 4, packet.data, sizeof(packet.data), SQLITE_STATIC) != SQLITE_OK)
|
||||
goto write_err;
|
||||
|
||||
while ((rc = sqlite3_step(insert_stmt)) == SQLITE_BUSY)
|
||||
;
|
||||
if (rc != SQLITE_DONE)
|
||||
goto write_err;
|
||||
|
||||
if (sqlite3_reset(insert_stmt) != SQLITE_OK)
|
||||
goto write_err;
|
||||
|
||||
if (sqlite3_clear_bindings(insert_stmt) != SQLITE_OK)
|
||||
goto write_err;
|
||||
|
||||
local_seq++;
|
||||
|
||||
printf("OK: seq=%d crc=%08x\n", current_seq, packet.crc);
|
||||
|
||||
/* send ACK reply */
|
||||
wpacket.type = 2;
|
||||
wpacket.pid = packet.pid;
|
||||
cobs_encode(wbuf, (char *)&wpacket, sizeof(wpacket));
|
||||
write(fd, wbuf, sizeof(wbuf));
|
||||
|
||||
it_err:
|
||||
/* Fixup buffer for next iteration */
|
||||
if (remaining-1 > 0) {
|
||||
printf(" ---memmove(buf=%p, first_nul+1=%p, remaining-1=%ld);-->\n", buf, first_nul+1, remaining-1);
|
||||
memmove(buf, first_nul+1, remaining-1);
|
||||
}
|
||||
//hexdump(buf, 80);
|
||||
fflush(stdout);
|
||||
printf("--- continuing wpos=%d->%d\n", wpos, (int)(remaining-1));
|
||||
wpos = remaining-1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
write_err:
|
||||
fprintf(stderr, "Error writing to database: %s\n", sqlite3_errmsg(db));
|
||||
sqlite3_close(db);
|
||||
return EXIT_FAILURE;
|
||||
|
||||
epoll_err:
|
||||
fprintf(stderr, "epoll error: %s\n", strerror(errno));
|
||||
|
||||
loop_err:
|
||||
sqlite3_close(db);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
139
hardware/fw/tw_test.py
Normal file
139
hardware/fw/tw_test.py
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
from time import time
|
||||
from binascii import hexlify
|
||||
import enum
|
||||
import struct
|
||||
import zlib
|
||||
import sys
|
||||
import sqlite3
|
||||
|
||||
import serial
|
||||
from cobs import cobs
|
||||
|
||||
|
||||
class CtrlPacketTypes(enum.Enum):
|
||||
RESET = 1
|
||||
ACK = 2
|
||||
RETRANSMIT = 3
|
||||
|
||||
def unpack_head(fmt, data):
|
||||
split = struct.calcsize(fmt)
|
||||
return [ *struct.unpack(fmt, data[:split]), data[split:] ]
|
||||
|
||||
def ctrl_packet(ptype, pid=0):
|
||||
return cobs.encode(struct.pack('BB', ptype.value, pid)) + b'\0'
|
||||
|
||||
ctrl_reset = lambda: ctrl_packet(CtrlPacketTypes.RESET)
|
||||
ctrl_ack = lambda pid: ctrl_packet(CtrlPacketTypes.ACK, pid)
|
||||
ctrl_retransmit = lambda pid: ctrl_packet(CtrlPacketTypes.RETRANSMIT, pid)
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument('-b', '--baudrate', type=int, default=250000)
|
||||
parser.add_argument('port', nargs='?', default=None)
|
||||
parser.add_argument('dbfile')
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.port is None:
|
||||
try:
|
||||
candidate, = os.listdir('/dev/serial/by-id')
|
||||
args.port = os.path.join('/dev/serial/by-id', candidate)
|
||||
print(f'No port given, guessing {args.port}')
|
||||
|
||||
except:
|
||||
print('No port given and could not guess port. Exiting.')
|
||||
sys.exit(1)
|
||||
|
||||
ser = serial.Serial(args.port, args.baudrate, timeout=1.0)
|
||||
db = sqlite3.connect(args.dbfile)
|
||||
db.execute('CREATE TABLE IF NOT EXISTS measurements (run_id INTEGER, rx_ts INTEGER, seq INTEGER, data BLOB)')
|
||||
db.execute('''CREATE TABLE IF NOT EXISTS errors (
|
||||
run_id INTEGER,
|
||||
rx_ts INTEGER,
|
||||
type TEXT,
|
||||
seq INTEGER,
|
||||
pid INTEGER,
|
||||
pid_expected INTEGER,
|
||||
crc32 INTEGER,
|
||||
crc32_expected INTEGER,
|
||||
data BLOB)''')
|
||||
run_id, = db.execute('SELECT IFNULL(MAX(run_id), -1) + 1 FROM measurements').fetchone()
|
||||
|
||||
ser.flushInput()
|
||||
ser.write(ctrl_reset())
|
||||
ser.flushOutput()
|
||||
|
||||
last_pid = None
|
||||
lines_written = 0
|
||||
cur = db.cursor()
|
||||
capture_start = time()
|
||||
while True:
|
||||
#ser.write(cobs.encode(b'\x01\xff') + b'\0')
|
||||
data = ser.read_until(b'\0')
|
||||
for data in data.split(b'\0')[:-1]: # data always ends on \0 due to read_until, so split off the trailing empty bytes()
|
||||
try:
|
||||
if not data:
|
||||
#print(f'{time():>7.3f} Timeout: resetting')
|
||||
#ser.write(cobs.encode(b'\x01\xff') + b'\0') # reset
|
||||
ser.write(ctrl_ack(0)) # FIXME delet this
|
||||
cur.execute('INSERT INTO errors(run_id, rx_ts, type) VALUES (?, ?, "retransmission")',
|
||||
(run_id, int(time()*1000)))
|
||||
continue
|
||||
|
||||
crc32, payload = unpack_head('I', cobs.decode(data))
|
||||
pid, seq, data = unpack_head('xBH', payload)
|
||||
ts = time()
|
||||
|
||||
# Calculate byte-wise CRC32
|
||||
our_crc = zlib.crc32(bytes(b for x in payload for b in (0, 0, 0, x)))
|
||||
#log.append((time(), seq, crc32, our_crc, pid, data))
|
||||
bars = '\u2581\u2582\u2583\u2584\u2585\u2586\u2587\u2588'
|
||||
sparkline = ''.join(bars[int(x/4096*8)] for x in struct.unpack('<32H', data))
|
||||
print(f'\033[38;5;249m{ts-capture_start:>10.3f}',
|
||||
f'\033[94m{seq:05d}',
|
||||
f'\033[38;5;243m{crc32:08x}',
|
||||
f'\033[38;5;243m{our_crc:08x}',
|
||||
f'\033[38;5;243m{pid}',
|
||||
f'\033[0m{hexlify(data).decode()}',
|
||||
f'\033[94m{sparkline}\033[0m', end='')
|
||||
|
||||
error = False
|
||||
suppress_ack = False
|
||||
if crc32 != our_crc:
|
||||
print(' \033[1;91mCRC ERROR\033[0m', end='')
|
||||
suppress_ack = True
|
||||
error = True
|
||||
|
||||
if last_pid is not None and pid != (last_pid+1)%8:
|
||||
print(' \033[1;93mPID ERROR\033[0m', end='')
|
||||
error = True
|
||||
else:
|
||||
last_pid = pid
|
||||
|
||||
if not suppress_ack:
|
||||
ser.write(ctrl_ack(pid))
|
||||
ser.flushOutput()
|
||||
|
||||
if not suppress_ack:
|
||||
cur.execute('INSERT INTO measurements VALUES (?, ?, ?, ?)', (run_id, int(ts*1000), seq, data))
|
||||
if error:
|
||||
cur.execute('INSERT INTO errors VALUES (?, ?, "pid", ?, ?, ?, ?, ?, ?)',
|
||||
(run_id, int(ts*1000), seq, pid, (last_pid+1)%8, crc32, our_crc, data))
|
||||
|
||||
print()
|
||||
lines_written += 1
|
||||
if lines_written == 80:
|
||||
lines_written = 0
|
||||
print('\033[2J\033[H', end='')
|
||||
delta = ts-capture_start
|
||||
print(f'\033[7mRun {run_id}, capturing for {delta//3600//24:> 3.0f}:{delta//3600%24:02.0f}:{delta//60%60:02.0f}:{delta%60:06.3f}\033[0m')
|
||||
db.commit()
|
||||
|
||||
except Exception as e:
|
||||
print(e, len(data))
|
||||
ser.write(ctrl_ack(0)) # FIXME delet this
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue