Working rainbow code
This commit is contained in:
parent
e1daad1f2e
commit
bf19c278a6
3 changed files with 122 additions and 137 deletions
Binary file not shown.
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
#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 (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;
|
||||
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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue