Add LED matrix control (12×8)
This commit is contained in:
parent
dc3ce3a777
commit
bc062dac9b
|
@ -1 +1 @@
|
||||||
84
|
129
|
||||||
|
|
83
src/led.c
83
src/led.c
|
@ -22,7 +22,7 @@
|
||||||
| (1 << PIN_LED_B_2 * 2) | (1 << PIN_LED_R_3 * 2) \
|
| (1 << PIN_LED_B_2 * 2) | (1 << PIN_LED_R_3 * 2) \
|
||||||
| (1 << PIN_LED_G_3 * 2) | (1 << PIN_LED_B_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
|
// 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
|
// 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
|
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_0, PIN_LED_G_0, PIN_LED_B_0,
|
||||||
PIN_LED_R_1, PIN_LED_G_1, PIN_LED_B_1,
|
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
|
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)
|
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];
|
for(int i = 0; i < LED_COLUMNS; i++)
|
||||||
|
{
|
||||||
|
uint16_t gamma_corrected = (uint16_t)LED_Data[r][i];
|
||||||
|
gamma_corrected *= gamma_corrected;
|
||||||
gamma_corrected >>= 16 - LED_BITS;
|
gamma_corrected >>= 16 - LED_BITS;
|
||||||
|
|
||||||
for(int j = 0; j < LED_BITS; j++)
|
for(int j = 0; j < LED_BITS; j++)
|
||||||
{
|
{
|
||||||
if(gamma_corrected & (1 << j))
|
if(gamma_corrected & (1 << j))
|
||||||
{
|
{
|
||||||
LED_DMABuffer[j] &= ~(1 << LED_Pins[i]);
|
LED_DMABuffer[r * (LED_BITS + 1) + j] &=
|
||||||
|
~(1 << LED_Pins[i]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LED_DMABuffer[j] |= 1 << LED_Pins[i];
|
LED_DMABuffer[r * (LED_BITS + 1) + j] |= 1 << LED_Pins[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Data to reset outputs after all data bits are sent
|
// Data to reset outputs after all data bits are sent
|
||||||
LED_DMABuffer[LED_BITS] = 0x0000;
|
LED_DMABuffer[r * (LED_BITS + 1) + LED_BITS] = LED_ODR_MASK;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void LED_StartBCM(void)
|
static void LED_StartBCM(int row)
|
||||||
{
|
{
|
||||||
// Reset DMA and timer
|
// Reset DMA and timer
|
||||||
TIM3->CR1 = 0x0000;
|
TIM3->CR1 = 0x0000;
|
||||||
|
@ -81,7 +86,7 @@ static void LED_StartBCM(void)
|
||||||
TIM3->DIER = TIM_DIER_UDE | TIM_DIER_CC1DE;
|
TIM3->DIER = TIM_DIER_UDE | TIM_DIER_CC1DE;
|
||||||
|
|
||||||
// DMA channel 3: Output data to port a on TIM3 update
|
// 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.
|
// 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.
|
// The first 4 are sent out with assembly before the first DMA transfer.
|
||||||
DMA1_Channel3->CNDTR = LED_BITS + 1 - 4;
|
DMA1_Channel3->CNDTR = LED_BITS + 1 - 4;
|
||||||
|
@ -101,7 +106,7 @@ static void LED_StartBCM(void)
|
||||||
| DMA_CCR_DIR | DMA_CCR_EN;
|
| DMA_CCR_DIR | DMA_CCR_EN;
|
||||||
|
|
||||||
// Send the 4 LSBs, set to zero at the end (so the timing is independent
|
// 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"
|
__asm__ volatile(".syntax unified\n"
|
||||||
"str %[d0], [%[odr]];"
|
"str %[d0], [%[odr]];"
|
||||||
"str %[d1], [%[odr]];"
|
"str %[d1], [%[odr]];"
|
||||||
|
@ -132,10 +137,10 @@ static void LED_StartBCM(void)
|
||||||
"str %[off], [%[odr]];"
|
"str %[off], [%[odr]];"
|
||||||
:
|
:
|
||||||
: [odr] "l" ((uint32_t)&(GPIOA->ODR)),
|
: [odr] "l" ((uint32_t)&(GPIOA->ODR)),
|
||||||
[d0] "r" (LED_DMABuffer[0]),
|
[d0] "r" (LED_DMABuffer[row * (LED_BITS + 1) + 0]),
|
||||||
[d1] "r" (LED_DMABuffer[1]),
|
[d1] "r" (LED_DMABuffer[row * (LED_BITS + 1) + 1]),
|
||||||
[d2] "r" (LED_DMABuffer[2]),
|
[d2] "r" (LED_DMABuffer[row * (LED_BITS + 1) + 2]),
|
||||||
[d3] "r" (LED_DMABuffer[3]),
|
[d3] "r" (LED_DMABuffer[row * (LED_BITS + 1) + 3]),
|
||||||
[off] "r" (LED_ODR_MASK)
|
[off] "r" (LED_ODR_MASK)
|
||||||
:);
|
:);
|
||||||
|
|
||||||
|
@ -143,13 +148,38 @@ static void LED_StartBCM(void)
|
||||||
TIM3->CR1 = TIM_CR1_ARPE | TIM_CR1_CEN;
|
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)
|
void LED_Init(void)
|
||||||
{
|
{
|
||||||
RCC->AHBENR |= RCC_AHBENR_GPIOAEN;
|
RCC->AHBENR |= RCC_AHBENR_GPIOAEN;
|
||||||
|
RCC->AHBENR |= RCC_AHBENR_GPIOFEN;
|
||||||
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
|
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
|
||||||
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
|
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->ODR |= LED_ODR_MASK;
|
||||||
|
GPIOA->PUPDR &= ~LED_MODER_MASK;
|
||||||
GPIOA->OTYPER |= LED_ODR_MASK;
|
GPIOA->OTYPER |= LED_ODR_MASK;
|
||||||
GPIOA->MODER = (GPIOA->MODER & ~LED_MODER_MASK) | LED_MODER;
|
GPIOA->MODER = (GPIOA->MODER & ~LED_MODER_MASK) | LED_MODER;
|
||||||
|
|
||||||
|
@ -169,7 +199,7 @@ void LED_Init(void)
|
||||||
NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);
|
NVIC_EnableIRQ(DMA1_Channel2_3_IRQn);
|
||||||
|
|
||||||
LED_RefreshDMABuffer();
|
LED_RefreshDMABuffer();
|
||||||
LED_StartBCM();
|
LED_StartBCM(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DMA1_Channel2_3_IRQHandler(void)
|
void DMA1_Channel2_3_IRQHandler(void)
|
||||||
|
@ -177,6 +207,23 @@ void DMA1_Channel2_3_IRQHandler(void)
|
||||||
// Interrupt when all bits have been sent
|
// Interrupt when all bits have been sent
|
||||||
DMA1->IFCR = DMA_IFCR_CTCIF3;
|
DMA1->IFCR = DMA_IFCR_CTCIF3;
|
||||||
|
|
||||||
// Start sending bits out again
|
static int current_row = 0;
|
||||||
LED_StartBCM();
|
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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
#include "pinning.h"
|
#include "pinning.h"
|
||||||
|
|
||||||
#define LED_BITS 12
|
#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);
|
void LED_Init(void);
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,12 @@
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
|
// Delay a bit to make programming easier
|
||||||
|
for(int i = 0; i < 30000; i++)
|
||||||
|
{
|
||||||
|
__asm__ volatile("nop");
|
||||||
|
}
|
||||||
|
|
||||||
LED_Init();
|
LED_Init();
|
||||||
|
|
||||||
while(1)
|
while(1)
|
||||||
|
|
|
@ -13,3 +13,7 @@
|
||||||
#define PIN_LED_R_3 10
|
#define PIN_LED_R_3 10
|
||||||
#define PIN_LED_G_3 13
|
#define PIN_LED_G_3 13
|
||||||
#define PIN_LED_B_3 14
|
#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
|
||||||
|
|
Loading…
Reference in a new issue