aboutsummaryrefslogtreecommitdiff
path: root/fw
diff options
context:
space:
mode:
Diffstat (limited to 'fw')
-rw-r--r--fw/.gitignore8
-rw-r--r--fw/Makefile10
-rw-r--r--fw/main.c213
3 files changed, 166 insertions, 65 deletions
diff --git a/fw/.gitignore b/fw/.gitignore
new file mode 100644
index 0000000..619130b
--- /dev/null
+++ b/fw/.gitignore
@@ -0,0 +1,8 @@
+*.o
+*.dot
+*.lst
+*.map
+*.svgz
+*.expand
+*.bin
+*.hex
diff --git a/fw/Makefile b/fw/Makefile
index 5496030..be6ff0b 100644
--- a/fw/Makefile
+++ b/fw/Makefile
@@ -9,20 +9,20 @@ OBJCOPY := arm-none-eabi-objcopy
OBJDUMP := arm-none-eabi-objdump
SIZE := arm-none-eabi-size
-CFLAGS = -Wall -std=gnu11 -Os -fdump-rtl-expand
+CFLAGS = -g -Wall -std=gnu11 -O0 -fdump-rtl-expand
CFLAGS += -mlittle-endian -mcpu=cortex-m0 -march=armv6-m -mthumb
-CFLAGS += -ffunction-sections -fdata-sections
+#CFLAGS += -ffunction-sections -fdata-sections
LDFLAGS = -nostartfiles
#LDFLAGS += -specs=rdimon.specs -DSEMIHOSTING
LDFLAGS += -Wl,-Map=main.map -nostdlib
-LDFLAGS += -Wl,--gc-sections
+#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
+CFLAGS += -DSTM32F030x6 -DHSE_VALUE=25000000
LDFLAGS += -Tstm32_flash.ld
CFLAGS += -I$(CMSIS_DEV_PATH)/Include -I$(CMSIS_PATH)/Include -I$(HAL_PATH)/Inc -Iconfig
@@ -36,9 +36,11 @@ all: main.elf main.pdf
%.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 >$@
diff --git a/fw/main.c b/fw/main.c
index 73e7280..7153d59 100644
--- a/fw/main.c
+++ b/fw/main.c
@@ -1,18 +1,26 @@
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstrict-aliasing"
#include <stm32f0xx.h>
-#include <stdint.h>
-#include <system_stm32f0xx.h>
#include <stm32f0xx_ll_utils.h>
#include <stm32f0xx_ll_spi.h>
+#pragma GCC diagnostic pop
+
+#include <system_stm32f0xx.h>
+
+#include <stdint.h>
#include <string.h>
#include <unistd.h>
/*
* Part number: STM32F030F4C6
*/
-void tick(void) {
- for(int i=0; i<50; i++)
- __asm__("nop");
+/* Wait for about 0.2us */
+static void tick(void) {
+ /* 1 */ /* 2 */ /* 3 */ /* 4 */ /* 5 */
+ /* 5 */ __asm__("nop"); __asm__("nop"); __asm__("nop"); __asm__("nop"); __asm__("nop");
+ /* 10 */ __asm__("nop"); __asm__("nop"); __asm__("nop"); __asm__("nop"); __asm__("nop");
}
void spi_send(int data) {
@@ -32,18 +40,117 @@ void strobe_leds(void) {
GPIOA->BSRR = GPIO_BSRR_BR_9;
}
-#define SR_COMM 0x0002
-#define SR_ERROR 0x0004
-#define SR_ID 0x0008
+static volatile unsigned int sys_time = 0;
+
+enum Segment { SegA, SegB, SegC, SegD, SegE, SegF, SegG, SegDP, nsegments };
+enum {
+ nrows = 4,
+ ncols = 8,
+ nbits = 10,
+};
+enum {
+ frame_size_words = nrows*ncols*nsegments/32,
+};
+struct framebuf {
+ /* Multiplexing order: first Digits, then Time/bits, last Segments */
+ uint32_t data[nbits*frame_size_words];
+ int brightness; /* 0 or 1; controls global brighntess control */
+};
+
+struct framebuf fb[2] = {0};
+struct framebuf *read_fb=fb+0, *write_fb=fb+1;
+volatile int led_state = 0;
+volatile enum { FB_WRITE, FB_UPDATE } fb_op;
+
+#define LED_COMM 0x0001
+#define LED_ERROR 0x0002
+#define LED_ID 0x0004
#define SR_ILED_HIGH 0x0080
#define SR_ILED_LOW 0x0040
+
+void shift_aux(int global_current, int leds, int active_segment) {
+ spi_send(
+ (global_current ? SR_ILED_HIGH : SR_ILED_LOW)
+ | (leds<<1)
+ | (0xff00 ^ (0x100<<active_segment)));
+ strobe_aux();
+}
+
+static volatile int frame_duration;
+/* returns new bit time in cycles */
+int shift_data() {
+ static int active_segment = 0;
+ static unsigned int active_bit = 0;
+ static int last_frame_sys_time;
+
+ /* Note: On boot, multiplexing will start with bit 1 due to the next few lines. This is perfectly ok. */
+ active_bit++;
+ if (active_bit == nbits) {
+ active_bit = 0;
+
+ active_segment++;
+ if (active_segment == nsegments) {
+ active_segment = 0;
+
+ int time = sys_time;
+ frame_duration = time - last_frame_sys_time;
+ last_frame_sys_time = sys_time;
+ if (fb_op == FB_UPDATE) {
+ struct framebuf *tmp = read_fb;
+ read_fb = write_fb;
+ write_fb = tmp;
+ fb_op = FB_WRITE;
+ }
+ }
+
+ shift_aux(read_fb->brightness, led_state, active_segment);
+ }
+
+ uint32_t current_word = read_fb->data[active_bit*frame_size_words + active_segment];
+ spi_send(current_word&0xffff);
+ spi_send(current_word>>16);
+ strobe_leds();
+
+ return 1<<active_bit;
+}
+
+void cfg_timer3() {
+ TIM3->CCMR1 = 6<<TIM_CCMR1_OC1M_Pos; /* PWM Mode 1 */
+ TIM3->CCER = TIM_CCER_CC1E | TIM_CCER_CC1P; /* Inverting output */
+ TIM3->DIER = TIM_DIER_CC1IE;
+ TIM3->CCR1 = 1000; /* Schedule first interrupt */
+ TIM3->PSC = SystemCoreClock/5000000 * 2; /* 0.40us/tick */
+ TIM3->ARR = 0xffff;
+ TIM3->EGR |= TIM_EGR_UG;
+ TIM3->CR1 = TIM_CR1_ARPE;
+ TIM3->CR1 |= TIM_CR1_CEN;
+}
+
+TIM_TypeDef *tim3 = TIM3;
+
+void TIM3_IRQHandler() {
+ //TIM3->CR1 &= ~TIM_CR1_CEN_Msk;
+
+ static int last_ivl;
+ last_ivl = TIM3->CNT;
+ /* This takes about 10us */
+ int period = shift_data();
+ static int ivl;
+ ivl = TIM3->CNT - last_ivl;
+ TIM3->CCR1 = period;
+ TIM3->CNT = 0xffff; /* To not enable OC1 right away */
+
+ TIM3->SR &= ~TIM_SR_CC1IF_Msk;
+ //TIM3->CR1 |= TIM_CR1_CEN;
+}
+
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 |= (2<<RCC_CFGR_PLLMUL_Pos) | RCC_CFGR_PLLSRC_HSE_PREDIV; /* PLL x4 */
+ RCC->CFGR &= ~RCC_CFGR_PLLMUL_Msk & ~RCC_CFGR_SW_Msk & ~RCC_CFGR_PPRE_Msk & ~RCC_CFGR_HPRE_Msk;
+ RCC->CFGR |= (1<<RCC_CFGR_PLLMUL_Pos) | RCC_CFGR_PLLSRC_HSE_PREDIV; /* PLL x4 -> 50.0MHz */
RCC->CFGR2 &= ~RCC_CFGR2_PREDIV_Msk;
- RCC->CFGR2 |= RCC_CFGR2_PREDIV_DIV2;
+ RCC->CFGR2 |= RCC_CFGR2_PREDIV_DIV2; /* prediv :2 -> 12.5MHz */
RCC->CR |= RCC_CR_PLLON;
while (!(RCC->CR&RCC_CR_PLLRDY));
RCC->CFGR |= (2<<RCC_CFGR_SW_Pos);
@@ -53,10 +160,11 @@ int main(void) {
RCC->AHBENR |= RCC_AHBENR_GPIOAEN;
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
+ RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
GPIOA->MODER |=
(2<<GPIO_MODER_MODER5_Pos) /* PA5 - SCLK */
- | (1<<GPIO_MODER_MODER6_Pos) /* PA6 - LED !OE */
+ | (2<<GPIO_MODER_MODER6_Pos) /* PA6 - LED !OE */
| (2<<GPIO_MODER_MODER7_Pos) /* PA7 - MOSI */
| (1<<GPIO_MODER_MODER9_Pos) /* PA9 - LED strobe */
| (1<<GPIO_MODER_MODER10_Pos);/* PA10 - Auxiliary strobe */
@@ -71,6 +179,7 @@ int main(void) {
GPIOA->AFR[0] |=
(0<<GPIO_AFRL_AFRL5_Pos) /* SPI1_SCK */
+ | (1<<GPIO_AFRL_AFRL6_Pos) /* TIM3_CH1 */
| (0<<GPIO_AFRL_AFRL7_Pos); /* SPI1_MOSI */
/* Configure SPI controller */
@@ -79,60 +188,41 @@ int main(void) {
SPI1->CR2 &= ~SPI_CR2_DS_Msk;
SPI1->CR2 |= LL_SPI_DATAWIDTH_16BIT;
/* FIXME maybe try w/o BIDI */
+ /* Baud rate PCLK/2 -> 25MHz */
SPI1->CR1 = SPI_CR1_BIDIMODE | SPI_CR1_BIDIOE | SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_SPE | (0<<SPI_CR1_BR_Pos) | SPI_CR1_MSTR | SPI_CR1_CPOL | SPI_CR1_CPHA;
- int val = 0xffff;
- int aval = 0x0000;
- GPIOA->BSRR = GPIO_BSRR_BR_6; /* OE */
- int j = 0;
- int ctr = 0;
- int q = 0;
- int bval = 0x400;
+ read_fb->brightness = 1;
+ for (int i=0; i<sizeof(read_fb->data)/sizeof(uint32_t); i++) {
+ read_fb->data[i] = 0xffffffff;
+ }
+ cfg_timer3();
+
+ NVIC_EnableIRQ(TIM3_IRQn);
+ NVIC_SetPriority(TIM3_IRQn, 2);
+
+ SysTick_Config(SystemCoreClock/1000); /* 1ms interval */
+
while (42) {
- for (int i=0; i<8; i++) {
- spi_send(1<<(ctr&3));
- spi_send(1<<((ctr>>1)&3));
- strobe_leds();
- //spi_send(0x0200 | bval | (0xff^(1<<i)));
- //spi_send((0xff^(1<<i))<<8);
- //spi_send(SR_COMM | SR_ILED_HIGH | 0xff);
- //spi_send(0x00ff ^ (1<<ctr) | (0x100<<ctr));
- spi_send(bval | (0xff00 ^ (0x100<<i)));
- strobe_aux();
- for(int i=0; i<10; i++)
- tick();
- j++;
- if (j == 1000) {
- j = 0;
- ctr++;
- if (ctr == 8) {
- ctr = 0;
- q++;
- if (q == 6)
- q = 0;
- }
- switch (q) {
- case 0:
- bval = SR_COMM | SR_ILED_LOW;
- break;
- case 1:
- bval = SR_ID | SR_ILED_LOW;
- break;
- case 2:
- bval = SR_ERROR | SR_ILED_LOW;
- break;
- case 3:
- bval = SR_COMM | SR_ILED_HIGH;
- break;
- case 4:
- bval = SR_ID | SR_ILED_HIGH;
- break;
- case 5:
- bval = SR_ERROR | SR_ILED_HIGH;
- break;
- }
+ led_state = (sys_time>>8)&7;
+
+ int ctr = sys_time>>2;
+ for (int bit=0, bmask=1; bit<nbits; bit++, bmask<<=1) {
+
+ int data = 0;
+ for (uint32_t ibit = 1, j=0; ibit; ibit<<=1, j++) {
+ int _100 = (1<<nbits);
+ int _1ff = (2*_100-1);
+ int val = (ctr + (j<<(nbits-5))) & _1ff;
+ val = val&_100 ? _1ff-val : val;
+ data |= val&bmask ? ibit : 0;
}
+
+ for (int seg=0; seg<frame_size_words; seg++)
+ write_fb->data[bit*frame_size_words + seg] = data;
}
+ fb_op = FB_UPDATE;
+ while (fb_op == FB_UPDATE)
+ ;
}
}
@@ -151,5 +241,6 @@ void PendSV_Handler(void) {
}
void SysTick_Handler(void) {
+ sys_time++;
}