#include #include #include #include #include /* * Part number: STM32F030F4C6 */ #define NBITS 12 void do_transpose(void); 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<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 |= (2< 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<AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN; RCC->APB2ENR |= RCC_APB2ENR_SPI1EN | RCC_APB2ENR_TIM1EN | RCC_APB2ENR_USART1EN | RCC_APB2ENR_ADCEN; GPIOA->MODER |= (3<MODER |= (2<OTYPER |= GPIO_OTYPER_OT_6; /* LED outputs -> open drain */ /* Set shift register IO GPIO output speed */ GPIOA->OSPEEDR |= (3<OSPEEDR |= (3<AFR[0] |= (1<AFR[1] |= (2<AFR[0] |= (2< 1MBd */ SPI1->CR1 = SPI_CR1_BIDIMODE | SPI_CR1_BIDIOE | SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_SPE | (0<CR2 = (0xf<CR1 = TIM_CR1_ARPE; // | TIM_CR1_OPM; // | TIM_CR1_URS; TIM1->PSC = 1; // debug /* CH2 - clear/!MR, CH3 - strobe/STCP */ TIM1->CCMR2 = (6<CCER |= TIM_CCER_CC3E | TIM_CCER_CC3NE | TIM_CCER_CC3P | TIM_CCER_CC3NP; TIM1->BDTR = TIM_BDTR_MOE | (8<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); TIM1->EGR |= TIM_EGR_UG; while (42) { #define HUE_MAX ((1<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) idx = 0; GPIOA->ODR ^= GPIO_ODR_6; /* LED1 */ /* 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); /* 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<ARR = period + timer_cycles_for_spi_transmissions; TIM1->CCR3 = timer_cycles_for_spi_transmissions; TIM1->SR &= ~TIM_SR_UIF_Msk; } void NMI_Handler(void) { } void HardFault_Handler(void) { for(;;); } void SVC_Handler(void) { } void PendSV_Handler(void) { } void SysTick_Handler(void) { } /* FIXME */ void _exit(void) {} void *__bss_start__; void *__bss_end__; int __errno;