diff --git a/build-number.txt b/build-number.txt index 871727d..b0d7324 100644 --- a/build-number.txt +++ b/build-number.txt @@ -1 +1 @@ -84 +129 diff --git a/src/led.c b/src/led.c index 430f1c6..73ba465 100644 --- a/src/led.c +++ b/src/led.c @@ -22,7 +22,7 @@ | (1 << PIN_LED_B_2 * 2) | (1 << PIN_LED_R_3 * 2) \ | (1 << PIN_LED_G_3 * 2) | (1 << PIN_LED_B_3 * 2)) -uint8_t LED_Data[LED_COUNT] = {0, 63, 200, 255}; +uint8_t LED_Data[LED_ROWS][LED_COLUMNS] = {{0}}; // TIM3 is clocked by APB1 and thus receives only half the system clock. The 4 // LSBs have bit lengths 2, 4, 8, and 16 cycles and are generated blocking from @@ -32,7 +32,7 @@ static const uint16_t LED_BitLengths[LED_BITS - 4] = 16, 32, 64, 128, 256, 512, 1024, 2048 }; -static const int LED_Pins[LED_COUNT] = +static const int LED_Pins[LED_COLUMNS] = { PIN_LED_R_0, PIN_LED_G_0, PIN_LED_B_0, PIN_LED_R_1, PIN_LED_G_1, PIN_LED_B_1, @@ -40,32 +40,37 @@ static const int LED_Pins[LED_COUNT] = PIN_LED_R_3, PIN_LED_G_3, PIN_LED_B_3 }; -static uint16_t LED_DMABuffer[LED_BITS + 1]; +static uint16_t LED_DMABuffer[LED_ROWS * (LED_BITS + 1)]; static void LED_RefreshDMABuffer(void) { - for(int i = 0; i < LED_COUNT; i++) + for(int r = 0; r < LED_ROWS; r++) { - uint16_t gamma_corrected = (uint16_t)LED_Data[i] * LED_Data[i]; - gamma_corrected >>= 16 - LED_BITS; - - for(int j = 0; j < LED_BITS; j++) + for(int i = 0; i < LED_COLUMNS; i++) { - if(gamma_corrected & (1 << j)) + uint16_t gamma_corrected = (uint16_t)LED_Data[r][i]; + gamma_corrected *= gamma_corrected; + gamma_corrected >>= 16 - LED_BITS; + + for(int j = 0; j < LED_BITS; j++) { - LED_DMABuffer[j] &= ~(1 << LED_Pins[i]); - } - else - { - LED_DMABuffer[j] |= 1 << LED_Pins[i]; + if(gamma_corrected & (1 << j)) + { + LED_DMABuffer[r * (LED_BITS + 1) + j] &= + ~(1 << LED_Pins[i]); + } + else + { + LED_DMABuffer[r * (LED_BITS + 1) + j] |= 1 << LED_Pins[i]; + } } } + // Data to reset outputs after all data bits are sent + LED_DMABuffer[r * (LED_BITS + 1) + LED_BITS] = LED_ODR_MASK; } - // Data to reset outputs after all data bits are sent - LED_DMABuffer[LED_BITS] = 0x0000; } -static void LED_StartBCM(void) +static void LED_StartBCM(int row) { // Reset DMA and timer TIM3->CR1 = 0x0000; @@ -81,7 +86,7 @@ static void LED_StartBCM(void) TIM3->DIER = TIM_DIER_UDE | TIM_DIER_CC1DE; // DMA channel 3: Output data to port a on TIM3 update - DMA1_Channel3->CMAR = (uint32_t)&(LED_DMABuffer[4]); + DMA1_Channel3->CMAR = (uint32_t)&(LED_DMABuffer[row * (LED_BITS + 1) + 4]); // One transfer for each bit plus one to set the outputs to zero again. // The first 4 are sent out with assembly before the first DMA transfer. DMA1_Channel3->CNDTR = LED_BITS + 1 - 4; @@ -101,7 +106,7 @@ static void LED_StartBCM(void) | DMA_CCR_DIR | DMA_CCR_EN; // Send the 4 LSBs, set to zero at the end (so the timing is independent - // from the delay intrudoced by the DMA) + // from the delay introduced by the DMA) __asm__ volatile(".syntax unified\n" "str %[d0], [%[odr]];" "str %[d1], [%[odr]];" @@ -132,10 +137,10 @@ static void LED_StartBCM(void) "str %[off], [%[odr]];" : : [odr] "l" ((uint32_t)&(GPIOA->ODR)), - [d0] "r" (LED_DMABuffer[0]), - [d1] "r" (LED_DMABuffer[1]), - [d2] "r" (LED_DMABuffer[2]), - [d3] "r" (LED_DMABuffer[3]), + [d0] "r" (LED_DMABuffer[row * (LED_BITS + 1) + 0]), + [d1] "r" (LED_DMABuffer[row * (LED_BITS + 1) + 1]), + [d2] "r" (LED_DMABuffer[row * (LED_BITS + 1) + 2]), + [d3] "r" (LED_DMABuffer[row * (LED_BITS + 1) + 3]), [off] "r" (LED_ODR_MASK) :); @@ -143,13 +148,38 @@ static void LED_StartBCM(void) TIM3->CR1 = TIM_CR1_ARPE | TIM_CR1_CEN; } +static inline void LED_PulseRowClock(void) +{ + __asm__ volatile("nop"); + GPIOF->BSRR = (1 << PIN_ROW_SCK); + __asm__ volatile("nop"); + GPIOF->BRR = (1 << PIN_ROW_SCK); +} + void LED_Init(void) { RCC->AHBENR |= RCC_AHBENR_GPIOAEN; + RCC->AHBENR |= RCC_AHBENR_GPIOFEN; RCC->AHBENR |= RCC_AHBENR_DMA1EN; RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; + GPIOF->ODR &= ~(1 << PIN_ROW_SCK) & ~(1 << PIN_ROW_DATA); + GPIOF->MODER = (GPIOF->MODER + & ~(0x3 << PIN_ROW_SCK * 2) & ~(0x3 << PIN_ROW_DATA * 2)) + | (0x1 << PIN_ROW_SCK * 2) | (0x1 << PIN_ROW_DATA * 2); + + // Reset the shift register. Since RCK and SCK are shorted together, one + // additional clock cycle is needed. + GPIOF->BSRR = (1 << PIN_ROW_DATA); + for(int i = 0; i < LED_ROWS + 1; i++) + { + LED_PulseRowClock(); + } + // All shift register outputs are now '1'. Because the rows are driven with + // external transistors, this means all rows are off. + GPIOA->ODR |= LED_ODR_MASK; + GPIOA->PUPDR &= ~LED_MODER_MASK; GPIOA->OTYPER |= LED_ODR_MASK; GPIOA->MODER = (GPIOA->MODER & ~LED_MODER_MASK) | LED_MODER; @@ -169,7 +199,7 @@ void LED_Init(void) NVIC_EnableIRQ(DMA1_Channel2_3_IRQn); LED_RefreshDMABuffer(); - LED_StartBCM(); + LED_StartBCM(0); } void DMA1_Channel2_3_IRQHandler(void) @@ -177,6 +207,23 @@ void DMA1_Channel2_3_IRQHandler(void) // Interrupt when all bits have been sent DMA1->IFCR = DMA_IFCR_CTCIF3; - // Start sending bits out again - LED_StartBCM(); + static int current_row = 0; + current_row++; + if(current_row >= LED_ROWS) + { + GPIOF->BRR = 1 << PIN_ROW_DATA; + current_row = 0; + LED_PulseRowClock(); + GPIOF->BSRR = 1 << PIN_ROW_DATA; + } + else + { + LED_PulseRowClock(); + } + + // Start sending bits out again. The row offset caused by the shift + // register "lagging" one clock cycle behind because RCK and SCK are + // connected to the same signal doesn't matter here: we're not paying much + // attention to which rows are which anyway. + LED_StartBCM(current_row); } diff --git a/src/led.h b/src/led.h index a0366ad..9fcd5db 100644 --- a/src/led.h +++ b/src/led.h @@ -4,7 +4,8 @@ #include "pinning.h" #define LED_BITS 12 -#define LED_COUNT 12 +#define LED_ROWS 8 // Rows are driven by a shift register +#define LED_COLUMNS 12 // Columns are driven by the MCU directly void LED_Init(void); diff --git a/src/main.c b/src/main.c index db35213..be6f4eb 100644 --- a/src/main.c +++ b/src/main.c @@ -2,6 +2,12 @@ int main(void) { + // Delay a bit to make programming easier + for(int i = 0; i < 30000; i++) + { + __asm__ volatile("nop"); + } + LED_Init(); while(1) diff --git a/src/pinning.h b/src/pinning.h index 41ee1a9..c10a259 100644 --- a/src/pinning.h +++ b/src/pinning.h @@ -13,3 +13,7 @@ #define PIN_LED_R_3 10 #define PIN_LED_G_3 13 #define PIN_LED_B_3 14 + +// Port F +#define PIN_ROW_DATA 0 // Shift register data in +#define PIN_ROW_SCK 1 // Shift register clock