diff options
author | jaseg <git@jaseg.net> | 2017-09-24 23:16:49 +0200 |
---|---|---|
committer | jaseg <git@jaseg.net> | 2017-09-24 23:16:49 +0200 |
commit | bf19c278a6a74a18e00cd576852817400cd6fed9 (patch) | |
tree | c4d5d3e6a64516c0ed45fac731bf1188f4bf8ea0 | |
parent | e1daad1f2e977aeea8dd0f227e3e4ef6def1b68a (diff) | |
download | olsndot-bf19c278a6a74a18e00cd576852817400cd6fed9.tar.gz olsndot-bf19c278a6a74a18e00cd576852817400cd6fed9.tar.bz2 olsndot-bf19c278a6a74a18e00cd576852817400cd6fed9.zip |
Working rainbow code
-rw-r--r-- | olsndot/PCB_Project/bom.ods | bin | 23580 -> 24867 bytes | |||
-rw-r--r-- | olsndot/firmware/Makefile | 9 | ||||
-rw-r--r-- | olsndot/firmware/main.c | 250 |
3 files changed, 122 insertions, 137 deletions
diff --git a/olsndot/PCB_Project/bom.ods b/olsndot/PCB_Project/bom.ods Binary files differindex 246dbf4..69c5fe4 100644 --- a/olsndot/PCB_Project/bom.ods +++ b/olsndot/PCB_Project/bom.ods diff --git a/olsndot/firmware/Makefile b/olsndot/firmware/Makefile index cf338eb..625865d 100644 --- a/olsndot/firmware/Makefile +++ b/olsndot/firmware/Makefile @@ -10,13 +10,13 @@ SIZE := arm-none-eabi-size CFLAGS = -Wall -g -std=gnu11 -Os CFLAGS += -mlittle-endian -mcpu=cortex-m0 -march=armv6-m -mthumb -CFLAGS += -ffunction-sections -fdata-sections -CFLAGS += -Wl,--gc-sections -Wl,-Map=main.map +CFLAGS += -ffunction-sections -fdata-sections -Wl,--gc-sections +CFLAGS += -Wl,-Map=main.map # 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=16000000 CFLAGS += -Tstm32_flash.ld CFLAGS += -I$(CMSIS_DEV_PATH)/Include -I$(CMSIS_PATH)/Include -I$(HAL_PATH)/Inc -Iconfig @@ -28,6 +28,9 @@ CFLAGS += -L$(CMSIS_PATH)/Lib/GCC -larm_cortexM0l_math all: main.elf +cmsis_exports.c: $(CMSIS_DEV_PATH)/Include/stm32f030x6.h $(CMSIS_PATH)/Include/core_cm0.h + python3 gen_cmsis_exports.py $^ > $@ + main.elf: main.c startup_stm32f030x6.s system_stm32f0xx.c $(HAL_PATH)/Src/stm32f0xx_ll_utils.c $(CC) $(CFLAGS) -o $@ $^ $(OBJCOPY) -O ihex $@ $(@:.elf=.hex) diff --git a/olsndot/firmware/main.c b/olsndot/firmware/main.c index e4b3935..1b3e054 100644 --- a/olsndot/firmware/main.c +++ b/olsndot/firmware/main.c @@ -3,26 +3,72 @@ #include <stdint.h> #include <system_stm32f0xx.h> #include <stm32f0xx_ll_utils.h> +#include <math.h> /* * Part number: STM32F030F4C6 */ #define NBITS 12 void do_transpose(void); -uint32_t brightness[8]; -volatile uint8_t brightness_by_bit[NBITS]; +uint32_t brightness[32]; +volatile uint32_t brightness_by_bit[NBITS]; + +void hsv_set(int idx, int hue, int white) { + int i = hue>>NBITS; + int j = hue & (~(-1<<NBITS)); + int r=0, g=0, b=0; + switch (i) { + case 0: + r = (1<<NBITS)-1; + g = 0; + b = j; + break; + case 1: + r = (1<<NBITS)-1-j; + g = 0; + b = (1<<NBITS)-1; + break; + case 2: + r = 0; + g = j; + b = (1<<NBITS)-1; + break; + case 3: + r = 0; + g = (1<<NBITS)-1; + b = (1<<NBITS)-1-j; + break; + case 4: + r = j; + g = (1<<NBITS)-1; + b = 0; + break; + case 5: + r = (1<<NBITS)-1; + g = (1<<NBITS)-1-j; + b = 0; + break; + } + brightness[idx*4 + 0] = white; + brightness[idx*4 + 1] = r; + brightness[idx*4 + 2] = g; + brightness[idx*4 + 3] = b; +} +int hue; 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; /* PLL x4 */ + RCC->CFGR &= ~RCC_CFGR_PLLMUL_Msk & ~RCC_CFGR_SW_Msk & ~RCC_CFGR_PPRE_Msk & ~RCC_CFGR_HPRE_Msk; + RCC->CFGR |= (2<<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; /* prediv :2 -> 12.5MHz */ RCC->CR |= RCC_CR_PLLON; while (!(RCC->CR&RCC_CR_PLLRDY)); RCC->CFGR |= (2<<RCC_CFGR_SW_Pos); SystemCoreClockUpdate(); - LL_Init1msTick(SystemCoreClock); + RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN; RCC->APB2ENR |= RCC_APB2ENR_SPI1EN | RCC_APB2ENR_TIM1EN | RCC_APB2ENR_USART1EN | RCC_APB2ENR_ADCEN; @@ -32,26 +78,26 @@ int main(void) { | (1<<GPIO_MODER_MODER1_Pos) /* PA1 - RS485 TX enable */ | (2<<GPIO_MODER_MODER2_Pos) /* PA2 - RS485 TX */ | (2<<GPIO_MODER_MODER3_Pos) /* PA3 - RS485 RX */ - | (1<<GPIO_MODER_MODER4_Pos) /* PA4 - LED1 open-drain output */ + /* PA4 reserved because */ | (2<<GPIO_MODER_MODER5_Pos) /* PA5 - Shift register clk/SCLK */ | (1<<GPIO_MODER_MODER6_Pos) /* PA6 - LED2 open-drain output */ | (2<<GPIO_MODER_MODER7_Pos) /* PA7 - Shift register data/MOSI */ - | (2<<GPIO_MODER_MODER9_Pos) /* PA9 - Shift register clear (TIM1_CH2) */ + | (2<<GPIO_MODER_MODER9_Pos) /* FIXME PA9 - Shift register clear (TIM1_CH2) */ | (2<<GPIO_MODER_MODER10_Pos);/* PA10 - Shift register strobe (TIM1_CH3) */ GPIOB->MODER |= - (1<<GPIO_MODER_MODER1_Pos); /* PB1 - Current measurement range selection */ + (2<<GPIO_MODER_MODER1_Pos); /* PB1 - Shift register clear (TIM1_CH3N) */ - GPIOA->OTYPER |= GPIO_OTYPER_OT_6 | GPIO_OTYPER_OT_4; /* LED outputs -> open drain */ + GPIOA->OTYPER |= GPIO_OTYPER_OT_6; /* LED outputs -> open drain */ /* Set shift register IO GPIO output speed */ GPIOA->OSPEEDR |= - (3<<GPIO_OSPEEDR_OSPEEDR4_Pos) /* LED1 */ - | (3<<GPIO_OSPEEDR_OSPEEDR5_Pos) /* SCLK */ - | (3<<GPIO_OSPEEDR_OSPEEDR6_Pos) /* LED2 */ + (3<<GPIO_OSPEEDR_OSPEEDR5_Pos) /* SCLK */ + | (3<<GPIO_OSPEEDR_OSPEEDR6_Pos) /* LED1 */ | (3<<GPIO_OSPEEDR_OSPEEDR7_Pos) /* MOSI */ - | (3<<GPIO_OSPEEDR_OSPEEDR9_Pos) /* Clear */ | (3<<GPIO_OSPEEDR_OSPEEDR10_Pos);/* Strobe */ + GPIOB->OSPEEDR |= + (3<<GPIO_OSPEEDR_OSPEEDR1_Pos); /* Clear */ GPIOA->AFR[0] |= (1<<GPIO_AFRL_AFRL2_Pos) /* USART1_TX */ @@ -59,38 +105,27 @@ int main(void) { | (0<<GPIO_AFRL_AFRL5_Pos) /* SPI1_SCK */ | (0<<GPIO_AFRL_AFRL7_Pos); /* SPI1_MOSI */ GPIOA->AFR[1] |= - (2<<GPIO_AFRH_AFRH1_Pos) /* TIM1_CH2 */ - | (2<<GPIO_AFRH_AFRH2_Pos); /* TIM1_CH3 */ + (2<<GPIO_AFRH_AFRH2_Pos); /* TIM1_CH3 */ + GPIOB->AFR[0] |= + (2<<GPIO_AFRL_AFRL1_Pos); /* TIM1_CH3N */ - GPIOB->BSRR = GPIO_BSRR_BR_1; /* clear output is active low */ - /* Configure SPI controller */ /* CPOL=0, CPHA=0, prescaler=8 -> 1MBd */ -// SPI1->CR1 = SPI_CR1_BIDIMODE | SPI_CR1_BIDIOE | SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_SPE | (2<<SPI_CR1_BR_Pos) | SPI_CR1_MSTR; 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; - SPI1->CR2 = (7<<SPI_CR2_DS_Pos); + SPI1->CR2 = (0xf<<SPI_CR2_DS_Pos); /* Configure TIM1 for display strobe generation */ /* Configure UART for RS485 comm */ /* 8N1, 115200Bd */ -// TIM1->CR1 = TIM_CR1_OPM | TIM_CR1_URS; -// TIM1->CR1 = TIM_CR1_ARPE | TIM_CR1_URS; - TIM1->CR1 = TIM_CR1_ARPE | TIM_CR1_OPM; // | TIM_CR1_URS; - TIM1->CR2 = 0; //TIM_CR2_CCPC; - TIM1->SMCR = 0; - TIM1->DIER = 0; + TIM1->CR1 = TIM_CR1_ARPE; // | TIM_CR1_OPM; // | TIM_CR1_URS; TIM1->PSC = 1; // debug /* CH2 - clear/!MR, CH3 - strobe/STCP */ - TIM1->CCR2 = 1; - TIM1->RCR = 0; - TIM1->CCMR1 = (6<<TIM_CCMR1_OC2M_Pos); // | TIM_CCMR1_OC2PE; TIM1->CCMR2 = (6<<TIM_CCMR2_OC3M_Pos); // | TIM_CCMR2_OC3PE; - TIM1->CCER |= TIM_CCER_CC2E | TIM_CCER_CC2NE | TIM_CCER_CC2P | TIM_CCER_CC3E; -// TIM1->CCMR1 = (6<<TIM_CCMR1_OC2M_Pos) | TIM_CCMR1_OC2PE; -// TIM1->CCMR2 = (6<<TIM_CCMR2_OC3M_Pos) | TIM_CCMR2_OC3PE; -// TIM1->CCER = TIM_CCER_CC2E | TIM_CCER_CC3E; -// TIM1->BDTR = TIM_BDTR_MOE; + TIM1->CCER |= TIM_CCER_CC3E | TIM_CCER_CC3NE | TIM_CCER_CC3P | TIM_CCER_CC3NP; + TIM1->BDTR = TIM_BDTR_MOE | (8<<TIM_BDTR_DTG_Pos); /* 1us dead time */ TIM1->DIER = TIM_DIER_UIE; + TIM1->ARR = 1; + TIM1->CR1 |= TIM_CR1_CEN; NVIC_EnableIRQ(TIM1_BRK_UP_TRG_COM_IRQn); NVIC_SetPriority(TIM1_BRK_UP_TRG_COM_IRQn, 2); @@ -98,88 +133,35 @@ int main(void) { TIM1->EGR |= TIM_EGR_UG; while (42) { - /* - for (uint8_t i=0; i<8; i++) { - brightness[1] = brightness[5] = i; - brightness[2] = brightness[6] = 0; - brightness[3] = brightness[7] = 0; - do_transpose(); - LL_mDelay(500); - } - for (uint8_t i=0; i<8; i++) { - brightness[1] = brightness[5] = 0; - brightness[2] = brightness[6] = i; - brightness[3] = brightness[7] = 0; - do_transpose(); - LL_mDelay(500); - } - for (uint8_t i=0; i<8; i++) { - brightness[1] = brightness[5] = 0; - brightness[2] = brightness[6] = 0; - brightness[3] = brightness[7] = i; - do_transpose(); - LL_mDelay(500); - } - for (uint8_t i=0; i<8; i++) { - brightness[1] = brightness[5] = i; - brightness[2] = brightness[6] = i; - brightness[3] = brightness[7] = i; +#define HUE_MAX ((1<<NBITS)*6) +#define HUE_OFFX 0.15F /* 0-1 */ +#define HUE_AMPLITUDE 0.05F /* 0-1 */ +#define CHANNEL_SPACING 1.5F /* in radians */ +#define WHITE 0.2F /* 0-1 */ + for (float v=0; v<8*M_PI; v += 0.01F) { + GPIOA->ODR ^= GPIO_ODR_6; + /* generate hsv fade */ + for (int ch=0; ch<8; ch++) { + hue = HUE_MAX * (HUE_OFFX + HUE_AMPLITUDE*sinf(v + ch*CHANNEL_SPACING)); + hue %= HUE_MAX; + hsv_set(ch, hue, WHITE*(1<<NBITS)); + } do_transpose(); - LL_mDelay(500); - } - } - { - */ - for (uint32_t i=0; i<6; i++) { - for (uint32_t j=0; j<(1<<NBITS); j++) { - GPIOA->ODR ^= GPIO_ODR_6; - switch (i) { - case 0: - brightness[1] = brightness[5] = (1<<NBITS)-1; - brightness[2] = brightness[6] = 0; - brightness[3] = brightness[7] = j; - break; - case 1: - brightness[1] = brightness[5] = (1<<NBITS)-1-j; - brightness[2] = brightness[6] = 0; - brightness[3] = brightness[7] = (1<<NBITS)-1; - break; - case 2: - brightness[1] = brightness[5] = 0; - brightness[2] = brightness[6] = j; - brightness[3] = brightness[7] = (1<<NBITS)-1; - break; - case 3: - brightness[1] = brightness[5] = 0; - brightness[2] = brightness[6] = (1<<NBITS)-1; - brightness[3] = brightness[7] = (1<<NBITS)-1-j; - break; - case 4: - brightness[1] = brightness[5] = j; - brightness[2] = brightness[6] = (1<<NBITS)-1; - brightness[3] = brightness[7] = 0; - break; - case 5: - brightness[1] = brightness[5] = (1<<NBITS)-1; - brightness[2] = brightness[6] = (1<<NBITS)-1-j; - brightness[3] = brightness[7] = 0; - break; - } - do_transpose(); - LL_mDelay(1); + for (int k=0; k<10000; k++) { + asm volatile("nop"); } } } } -uint32_t brightness[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; -volatile uint8_t brightness_by_bit[NBITS] = { 0 }; +uint32_t brightness[32] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +volatile uint32_t brightness_by_bit[NBITS] = { 0 }; void do_transpose(void) { for (uint32_t i=0; i<NBITS; i++) { - uint8_t bv = 0; + uint32_t bv = 0; uint32_t mask = 1<<i; - for (uint32_t j=0; j<8; j++) { + for (uint32_t j=0; j<32; j++) { if (brightness[j] & mask) bv |= 1<<j; } @@ -187,44 +169,38 @@ void do_transpose(void) { } } -/* - * 460ns - * 720ns - */ - -/* - * 1.00us - * 1.64us - * 2.84us - * 5.36us - * 10.4us - * 20.4us - * 40.4us - * 80.8us - */ void TIM1_BRK_UP_TRG_COM_IRQHandler(void) { + /* The index of the currently active bit. On entry of this function, this is the bit index of the upcoming period. + * On exit it is the index of the *next* period. */ static uint32_t idx = 0; + + /* Access bits offset by one as we are setting the *next* period based on idx below. */ + uint32_t val = brightness_by_bit[idx]; + idx++; if (idx >= NBITS) idx = 0; - GPIOA->ODR ^= GPIO_ODR_4; - TIM1->CCMR1 = (4<<TIM_CCMR1_OC2M_Pos); // | TIM_CCMR1_OC2PE; + GPIOA->ODR ^= GPIO_ODR_6; /* LED1 */ - SPI1->DR = brightness_by_bit[idx]<<8; + /* Shift out the current period's data. The shift register clear and strobe lines are handled by the timers + * capture/compare channel 3 complementary outputs. The dead-time generator is used to sequence the clear and strobe + * edges one after another. Since there may be small variations in IRQ service latency it is critical to allow for + * some leeway between the end of this data transmission and strobe and clear. */ + SPI1->DR = (val&0xffff); + while (SPI1->SR & SPI_SR_BSY); + SPI1->DR = (val>>16); while (SPI1->SR & SPI_SR_BSY); - const uint32_t period_base = 4; /* 1us */ - const uint32_t period = period_base<<idx; -// TIM1->BDTR = TIM_BDTR_MOE | (16<<TIM_BDTR_DTG_Pos); - TIM1->BDTR = TIM_BDTR_MOE | (0<<TIM_BDTR_DTG_Pos); - TIM1->CCR3 = period-1; - TIM1->CNT = period-1; - TIM1->ARR = period; - TIM1->CCMR1 = (6<<TIM_CCMR1_OC2M_Pos); // | TIM_CCMR1_OC2PE; - TIM1->EGR |= TIM_EGR_UG; - TIM1->ARR = 2; - TIM1->CR1 |= TIM_CR1_CEN; + /* Set up everything for the *next* period. The timer is set to count from 0 to ARR. ARR and CCR3 are pre-loaded, so + * the values written above will only be latched on timer overrun at the end of this period. This is a little + * complicated, but doing it this way has the advantage of keeping both duty cycle and frame rate precisely + * constant. */ + const int period_base = 4; /* 1us */ + const int period = (period_base<<idx) + 4 /* 1us dead time */; + const int timer_cycles_for_spi_transmissions = 128; + TIM1->ARR = period + timer_cycles_for_spi_transmissions; + TIM1->CCR3 = timer_cycles_for_spi_transmissions; TIM1->SR &= ~TIM_SR_UIF_Msk; } @@ -245,3 +221,9 @@ void PendSV_Handler(void) { void SysTick_Handler(void) { } +/* FIXME */ +void _exit(void) {} +void *__bss_start__; +void *__bss_end__; + +int __errno; |