-
Notifications
You must be signed in to change notification settings - Fork 0
/
leds.c
232 lines (184 loc) · 8.1 KB
/
leds.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
#include "leds.h"
/* Buffer that holds the LED information. *
* Format = [[GRB], [GRB], ...] */
volatile uint8_t ucLeds[NUMBER_OF_LEDS][COLOR_CHANNELS] = {{ 0 }};
/* Buffer that hold the duty cycle information. */
static uint16_t usLedDutyCycleBuffer[TOTAL_PERIODS] = { 0 };
/* Task handle. */
TaskHandle_t xUpdateLedsHandle = NULL;
/* Available stack size. */
UBaseType_t xLedsAvailableStack = 0;
/**
* @brief Task that initiates the PWM stream to the individually addressable LEDs.
* @note Executes every 10ms resulting in a 100Hz update rate.
* @retval None
*/
void vUpdateLedStrip( void * pvParameters )
{
TickType_t xLastWakeTime;
const TickType_t xFrequency = 10;
/* Initialize the xLastWakeTime variable with the current time. */
xLastWakeTime = xTaskGetTickCount();
/* Initialize start time.*/
uint16_t usStartTime = TIM12->CNT;
/* Create variable to hold the buffer index. */
uint16_t usBufferIndex = 0;
while ( 1 )
{
usBufferIndex = 0;
/* Fill transmit buffer with correct compare values to achieve the
correct pulse widths according to color values */
for (uint16_t usI = 0; usI < NUMBER_OF_LEDS; usI++)
{
for (uint8_t ucJ = 0; ucJ < COLOR_CHANNELS; ucJ++)
{
for (uint8_t ucK = 0; ucK < 8; ucK++)
{
uint8_t ucBit = ( ucLeds[usI][ucJ] & (0x80 >> ucK) ) >> (7 - ucK);
usLedDutyCycleBuffer[usBufferIndex] = LOW_THRESH + ucBit * ( (uint16_t)HIGH_THRESH - LOW_THRESH );
usBufferIndex++;
}
}
}
/* Set TIM4 CNT so an interrupt occurs to transfer the first byte. */
TIM4->CNT = CLOCK_THRESH-1;
/* Reset the pin used for transferring data. */
GPIOB->ODR &= ~(0x01<<6);
/* Clear the DMA flags. */
DMA_ClearFlag(DMA1_Stream0,DMA_FLAG_TCIF0);
DMA_ClearFlag(DMA1_Stream0,DMA_FLAG_HTIF0);
DMA_ClearFlag(DMA1_Stream0,DMA_FLAG_FEIF0);
/* Set the number of bytes to be transferred */
DMA1_Stream0->NDTR = TOTAL_PERIODS;
/* Start the DMA peripheral. */
DMA_Cmd(DMA1_Stream0, ENABLE);
/* Start the timer used for generating the PWM. */
TIM_Cmd(TIM4, ENABLE);
/* Check the stack size. */
xLedsAvailableStack = uxTaskGetStackHighWaterMark( xUpdateLedsHandle );
if (xLedsAvailableStack <= 20)
{
/* Turn orangle LED on if stack overflow is imminent/detected. */
STM_EVAL_LEDOn( LED3 );
}
/* Update the high water mark for task length. */
if ( (uint16_t)TIM12->CNT - usStartTime > xDebugStats.usPatternClocks )
{
xDebugStats.usUpdateLEDsClocks = (uint16_t)TIM12->CNT - usStartTime;
}
/* Wait for the next cycle. */
vTaskDelayUntil( &xLastWakeTime, xFrequency );
/* Initialize the start time. */
usStartTime = TIM12->CNT;
}
}
/*-----------------------------------------------------------*/
/**
* @brief Interrupt to handle the end of the DMA stream to the LEDs.
* @param None
* @retval None
*/
uint32_t ulGetRandVal( void )
{
return RNG_GetRandomNumber();
}
/*-----------------------------------------------------------*/
/**
* @brief Interrupt to handle the end of the DMA stream to the LEDs.
* @param None
* @retval None
*/
void DMA1_Stream0_IRQHandler()
{
/* Disable interrupts and other tasks from running during this interrupt. */
UBaseType_t uxSavedInterruptStatus = taskENTER_CRITICAL_FROM_ISR();
if( DMA_GetFlagStatus( DMA1_Stream0, DMA_FLAG_TCIF0 ) )
{
/* Clear DMA interrupt flags */
DMA_ClearFlag( DMA1_Stream0, DMA_FLAG_TCIF0 );
DMA_ClearFlag( DMA1_Stream0, DMA_FLAG_HTIF0 );
DMA_ClearFlag( DMA1_Stream0, DMA_FLAG_FEIF0 );
/* Disable timer 4 */
TIM_Cmd( TIM4, DISABLE );
/* Disable DMA stream 0 */
DMA_Cmd( DMA1_Stream0, DISABLE );
/* Reset the B6 pin to off. */
GPIOB->ODR &= ~( 0x01 << 6 );
}
/* Re-enable interrupts and other tasks. */
taskEXIT_CRITICAL_FROM_ISR( uxSavedInterruptStatus );
}
/*-----------------------------------------------------------*/
/**
* @brief Initialize the PWM stream to the individually addressable LEDs.
* @param None
* @retval None
*/
void vInitLeds(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the clock used for DMA. */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE); // 168 MHz
/* Enable the clock used for GPIOB. */
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); // 168 MHz
/* GPIO Configuration. */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_25MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/* Connect TIM4 pins to AF */
GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_TIM4);
/* Enable the clock used for TIM4 (42MHz) */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = CLOCK_THRESH;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
/* PWM1 Mode configuration: Channel 1 */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = (uint32_t) 0;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM4, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable);
/* DMA1 channel 0 configuration. */
DMA_DeInit(DMA1_Stream0);
DMA_InitStructure.DMA_Channel = DMA_Channel_2;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&TIM4->CCR1; // physical address of register to be loaded
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&usLedDutyCycleBuffer[0]; // physical address of data to load into register
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral; // shift data from memory to peripheral
DMA_InitStructure.DMA_BufferSize = 0;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // automatically increase buffer index
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 16 bits
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // 16 bits
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal; // stop DMA feed after buffer size is reached
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA1_Stream0, &DMA_InitStructure);
DMA_ITConfig(DMA1_Stream0, DMA_IT_TC, ENABLE);
DMA_ClearFlag(DMA1_Stream0, DMA_FLAG_TCIF0);
TIM_DMAConfig(TIM4, TIM_DMABase_CCR1, TIM_DMABurstLength_1Byte);
TIM_SelectCCDMA(TIM4, ENABLE);
TIM_DMACmd(TIM4, TIM_DMA_CC1, ENABLE);
/* Set DMA interrupt when buffer transfers are complete. */
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Stream0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY + 2U;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
/* Initalize the random number generator. */
/* Call RNG_GetRandomNumber(void) to get a random number. */
RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_RNG, ENABLE);
RNG_Cmd(ENABLE);
}
/*-----------------------------------------------------------*/