Newer
Older
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
/* Includes ------------------------------------------------------------------*/
#include "stm32f3xx_hal.h"
#include "globals.h"
/* USER CODE BEGIN Includes */
#include "led_driver.h"
/* USER CODE END Includes */
// Format Image data so that it can be pushed as is by DMA toward the GPIO
// Starting from image in raw format RGB.
// lines from left to right and top to bottom
// NUCLEO 64 avec 446
// to be put in port B0..10,12..15
// and PC0..PC4
// Sequence de pilotage des leds
// 8 bits G7..0, 8 bits R7..0, 8 bits B7..0
// Encodage des bits
// Bit 0 : 100
// Bit 1 : 110
// To be called just once
// This is actually supposed to be done at the end, but that's ok this way
void initLedResetCodeInMemoryForDMA2(uint8_t *portamem)
{
uint32_t i;
// pre fill never changing parts
for (i=0;i<NB_LEDS_PER_STRIP*8*3;i++)
{
*portamem++=0xFF;// pre fill, never changes
*portamem++=0x00;// will be overwritten
*portamem++=0x00;// pre fill, never changes
}
for (i=0;i<LED_RESET_DELAY;i++)
{
*portamem++ = 0;
}
}
uint8_t btable[32] =
{
0,1,2,3,4,5,6,7,
0,1,2,3,4,5,6,7,
0,1,2,3,4,5,6,7,
0,1,2,3,4,5,6,7
};
uint16_t bbintable[32];
// 0 for PORTA,
// 1 for PORTB,
// 2 for PORTC
// 3 for PORTD
uint8_t ptable[32] =
{
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
};
// 0 <= integer_offset < NB_LEDS_PER_STRIP required
// Version rerouting bits
// Une part de la compxit de ce code vient de ce qu'il ralise un offset entier integer_offset horizontal sur l'image, il doit grer le rebouclage image
// R,G,B,R,G,B,R,G,B : raw image
// G,R,B,G,R,B,G,R,B : how it must be sent to WS2812B
const int8_t rgbtrip[3] = {-1,2,2}; // starts with G then R (-1) then B (+2) then again +2 to reach next G
__attribute__((section("ccmram")))
void formatMemoryForDMA2(uint8_t *raw, uint8_t *portamem, uint8_t integer_offset)
{
uint8_t line,column,rgb_bit;
uint8_t *rgb[NB_LINES];
uint8_t tmpmem[4];
uint8_t rgbsequence;
static uint8_t initialized = 0;
if (initialized==0)
{
for (line=0;line<32;line++)
{
bbintable[line] = 1 << btable[line];
}
initialized = 1;
}
// calcul pour chaque ligne l'adresse de dbut
for (line=0;line<NB_LINES;line++)
{
rgb[line] = raw+1+integer_offset*3; // Starts with G, hence the +1
// Go to next Line
raw+=NB_LEDS_PER_STRIP*3;
}
portamem++; // skip first pre filled =0xFFFF;
for (column=0;column<NB_LEDS_PER_STRIP;column++)
{
// avec l'integer_offset, si on arrive au bout de la bande, il faut revenir au dbut
// seulement une fois.
// si integer_offset==1, a se fait juste la dernire colonne : column==NB_LEDS_PER_STRIP-1
if (column+integer_offset==NB_LEDS_PER_STRIP)
{
for (line=0;line<NB_LINES;line++)
{
rgb[line]-=NB_LEDS_PER_STRIP*3;
}
}
// Send the 3 colors for the column
for (rgbsequence=0;rgbsequence<3;rgbsequence++)
{
// 3x G then R then B
rgb_bit = 0x80;
while(rgb_bit)
{
// PORTB&C
// portamem++; // skip =0xFFFF;, already done
// portcmem++; // skip =0xFFFF;, already done
tmpmem[0] = 0;
tmpmem[1] = 0;
// PORTB&C 0..NB_LINES
for (line=0;line<NB_LINES;line++)
{
if ((*rgb[line])&rgb_bit)
{
tmpmem[ptable[line]] |= bbintable[line]; // routing according to printed circuit board
}
}
*portamem = tmpmem[0];
portamem+=3; // skip =0x0000, 0xFFFF;
rgb_bit = rgb_bit >> 1; // Bit 7 Bit 0.
}
for (line=0;line<NB_LINES;line++)
{
rgb[line]+=rgbtrip[rgbsequence]; // next is R, next time +=2, then +=2
}
}
}
}
extern DMA_HandleTypeDef hdma_tim2_ch1;
extern TIM_HandleTypeDef htim2;
void dmaLedDrive2(uint8_t *portamem)
{
GPIOA->ODR = 0;
HAL_DMA_Start(&hdma_tim2_ch1, (uint32_t) portamem, (uint32_t) &GPIOA->ODR, NB_DMA_WRITES);
// pourrait s'avrer ncessaire d'interrompre les interrupions entre ces deux instructions pour viter un dphasage.
// pas de problme, rien ne se passe avant que le timer 8 ne soit dmarr.
__HAL_TIM_ENABLE_DMA(&htim2, TIM_DMA_CC1);
// Appelle pas ces fonction, pour ne pas dmarrer le timer avant la fin de la config
// HAL_TIM_OC_Start(&htim8,TIM_CHANNEL_1); //Dmarre aussi le Timer 8
// HAL_TIM_OC_Start(&htim8,TIM_CHANNEL_3);
// Ici on n'a qu'un seul channel, alors, c'est OK aussi
TIM_CCxChannelCmd(TIM2, TIM_CHANNEL_1, TIM_CCx_ENABLE);
if(IS_TIM_BREAK_INSTANCE(TIM2) != RESET)
{
/* Enable the main output */
__HAL_TIM_MOE_ENABLE(&htim2);
}
/* Enable the Peripheral */
__HAL_TIM_ENABLE(&htim2);
}
void dmaPostLedDrive(void)
{
// Il faut dsactiver le CC0 avant le timer lui mme. Sinon, __HAL_TIM_DISABLE est non fonctionnel.
TIM_CCxChannelCmd(TIM2, TIM_CHANNEL_1, TIM_CCx_DISABLE);
__HAL_TIM_MOE_DISABLE(&htim2);
__HAL_TIM_DISABLE(&htim2);
__HAL_TIM_DISABLE_DMA(&htim2, TIM_DMA_CC1);
}
void dmaWait(void)
{
HAL_StatusTypeDef dma_state;
dma_state = HAL_DMA_PollForTransfer(&hdma_tim2_ch1, HAL_DMA_FULL_TRANSFER, 20);
if (dma_state == HAL_OK)
{
dmaPostLedDrive(); // 1s ou moins
}
else
crash(CC_DMA_TIM2_CH1_FAILED);
}
// Only one case : in case of loss of power
void dmaUrgentStop(void)
{
__HAL_DMA_DISABLE(&hdma_tim2_ch1);
HAL_DMA_Abort(&hdma_tim2_ch1);
// dmaPostLedDrive();
}