Mercurial > hg > beaglert
comparison core/PRU.cpp @ 0:8a575ba3ab52
Initial commit.
author | andrewm |
---|---|
date | Fri, 31 Oct 2014 19:10:17 +0100 |
parents | |
children | a6beeba3a648 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:8a575ba3ab52 |
---|---|
1 /* | |
2 * PRU.cpp | |
3 * | |
4 * Code for communicating with the Programmable Realtime Unit (PRU) | |
5 * on the BeagleBone AM335x series processors. The PRU loads and runs | |
6 * a separate code image compiled from an assembly file. Here it is | |
7 * used to handle audio and SPI ADC/DAC data. | |
8 * | |
9 * This code is specific to the PRU code in the assembly file; for example, | |
10 * it uses certain GPIO resources that correspond to that image. | |
11 * | |
12 * Created on: May 27, 2014 | |
13 * Author: andrewm | |
14 */ | |
15 | |
16 #include "../include/PRU.h" | |
17 #include "../include/prussdrv.h" | |
18 #include "../include/pruss_intc_mapping.h" | |
19 #include "../include/GPIOcontrol.h" | |
20 #include "../include/render.h" | |
21 | |
22 #include <iostream> | |
23 #include <stdlib.h> | |
24 #include <cstdio> | |
25 #include <cerrno> | |
26 #include <fcntl.h> | |
27 #include <sys/mman.h> | |
28 | |
29 // Xenomai-specific includes | |
30 #include <sys/mman.h> | |
31 #include <native/task.h> | |
32 #include <native/timer.h> | |
33 #include <rtdk.h> | |
34 | |
35 using namespace std; | |
36 | |
37 #define PRU_MEM_MCASP_OFFSET 0x2000 // Offset within PRU-SHARED RAM | |
38 #define PRU_MEM_MCASP_LENGTH 0x2000 // Length of McASP memory, in bytes | |
39 #define PRU_MEM_DAC_OFFSET 0x0 // Offset within PRU0 RAM | |
40 #define PRU_MEM_DAC_LENGTH 0x2000 // Length of ADC+DAC memory, in bytes | |
41 #define PRU_MEM_COMM_OFFSET 0x0 // Offset within PRU-SHARED RAM | |
42 | |
43 #define PRU_SHOULD_STOP 0 | |
44 #define PRU_CURRENT_BUFFER 1 | |
45 #define PRU_BUFFER_FRAMES 2 | |
46 #define PRU_SHOULD_SYNC 3 | |
47 #define PRU_SYNC_ADDRESS 4 | |
48 #define PRU_SYNC_PIN_MASK 5 | |
49 #define PRU_LED_ADDRESS 6 | |
50 #define PRU_LED_PIN_MASK 7 | |
51 #define PRU_FRAME_COUNT 8 | |
52 #define PRU_USE_SPI 9 | |
53 | |
54 #define PRU_SAMPLE_INTERVAL_NS 45351 // 22050Hz per SPI sample = 45.351us | |
55 | |
56 #define GPIO0_ADDRESS 0x44E07000 | |
57 #define GPIO1_ADDRESS 0x4804C000 | |
58 #define GPIO_SIZE 0x198 | |
59 #define GPIO_CLEARDATAOUT (0x190 / 4) | |
60 #define GPIO_SETDATAOUT (0x194 / 4) | |
61 | |
62 #define TEST_PIN_GPIO_BASE GPIO0_ADDRESS // Use GPIO0(31) for debugging | |
63 #define TEST_PIN_MASK (1 << 31) | |
64 #define TEST_PIN2_MASK (1 << 26) | |
65 | |
66 #define USERLED3_GPIO_BASE GPIO1_ADDRESS // GPIO1(24) is user LED 3 | |
67 #define USERLED3_PIN_MASK (1 << 24) | |
68 | |
69 const unsigned int PRU::kPruGPIODACSyncPin = 5; // GPIO0(5); P9-17 | |
70 const unsigned int PRU::kPruGPIOADCSyncPin = 48; // GPIO1(16); P9-15 | |
71 | |
72 const unsigned int PRU::kPruGPIOTestPin = 60; // GPIO1(28); P9-12 | |
73 const unsigned int PRU::kPruGPIOTestPin2 = 31; // GPIO0(31); P9-13 | |
74 const unsigned int PRU::kPruGPIOTestPin3 = 26; // GPIO0(26); P8-14 | |
75 | |
76 extern int gShouldStop; | |
77 extern int gRTAudioVerbose; | |
78 | |
79 // Constructor: specify a PRU number (0 or 1) | |
80 PRU::PRU() | |
81 : pru_number(0), running(false), spi_enabled(false), gpio_enabled(false), led_enabled(false), | |
82 gpio_test_pin_enabled(false), xenomai_gpio_fd(-1), xenomai_gpio(0) | |
83 { | |
84 | |
85 } | |
86 | |
87 // Destructor | |
88 PRU::~PRU() | |
89 { | |
90 if(running) | |
91 disable(); | |
92 if(gpio_enabled) | |
93 cleanupGPIO(); | |
94 if(xenomai_gpio_fd >= 0) | |
95 close(xenomai_gpio_fd); | |
96 } | |
97 | |
98 // Prepare the GPIO pins needed for the PRU | |
99 // If include_test_pin is set, the GPIO output | |
100 // is also prepared for an output which can be | |
101 // viewed on a scope. If include_led is set, | |
102 // user LED 3 on the BBB is taken over by the PRU | |
103 // to indicate activity | |
104 int PRU::prepareGPIO(int use_spi, int include_test_pin, int include_led) | |
105 { | |
106 if(use_spi) { | |
107 // Prepare DAC CS/ pin: output, high to begin | |
108 if(gpio_export(kPruGPIODACSyncPin)) { | |
109 if(gRTAudioVerbose) | |
110 cout << "Warning: couldn't export DAC sync pin\n"; | |
111 } | |
112 if(gpio_set_dir(kPruGPIODACSyncPin, OUTPUT_PIN)) { | |
113 if(gRTAudioVerbose) | |
114 cout << "Couldn't set direction on DAC sync pin\n"; | |
115 return -1; | |
116 } | |
117 if(gpio_set_value(kPruGPIODACSyncPin, HIGH)) { | |
118 if(gRTAudioVerbose) | |
119 cout << "Couldn't set value on DAC sync pin\n"; | |
120 return -1; | |
121 } | |
122 | |
123 // Prepare ADC CS/ pin: output, high to begin | |
124 if(gpio_export(kPruGPIOADCSyncPin)) { | |
125 if(gRTAudioVerbose) | |
126 cout << "Warning: couldn't export ADC sync pin\n"; | |
127 } | |
128 if(gpio_set_dir(kPruGPIOADCSyncPin, OUTPUT_PIN)) { | |
129 if(gRTAudioVerbose) | |
130 cout << "Couldn't set direction on ADC sync pin\n"; | |
131 return -1; | |
132 } | |
133 if(gpio_set_value(kPruGPIOADCSyncPin, HIGH)) { | |
134 if(gRTAudioVerbose) | |
135 cout << "Couldn't set value on ADC sync pin\n"; | |
136 return -1; | |
137 } | |
138 | |
139 spi_enabled = true; | |
140 } | |
141 | |
142 if(include_test_pin) { | |
143 // Prepare GPIO test output (for debugging), low to begin | |
144 if(gpio_export(kPruGPIOTestPin)) { | |
145 if(gRTAudioVerbose) | |
146 cout << "Warning: couldn't export GPIO test pin\n"; | |
147 } | |
148 if(gpio_set_dir(kPruGPIOTestPin, OUTPUT_PIN)) { | |
149 if(gRTAudioVerbose) | |
150 cout << "Couldn't set direction on GPIO test pin\n"; | |
151 return -1; | |
152 } | |
153 if(gpio_set_value(kPruGPIOTestPin, LOW)) { | |
154 if(gRTAudioVerbose) | |
155 cout << "Couldn't set value on GPIO test pin\n"; | |
156 return -1; | |
157 } | |
158 | |
159 if(gpio_export(kPruGPIOTestPin2)) { | |
160 if(gRTAudioVerbose) | |
161 cout << "Warning: couldn't export GPIO test pin 2\n"; | |
162 } | |
163 if(gpio_set_dir(kPruGPIOTestPin2, OUTPUT_PIN)) { | |
164 if(gRTAudioVerbose) | |
165 cout << "Couldn't set direction on GPIO test pin 2\n"; | |
166 return -1; | |
167 } | |
168 if(gpio_set_value(kPruGPIOTestPin2, LOW)) { | |
169 if(gRTAudioVerbose) | |
170 cout << "Couldn't set value on GPIO test pin 2\n"; | |
171 return -1; | |
172 } | |
173 | |
174 if(gpio_export(kPruGPIOTestPin3)) { | |
175 if(gRTAudioVerbose) | |
176 cout << "Warning: couldn't export GPIO test pin 3\n"; | |
177 } | |
178 if(gpio_set_dir(kPruGPIOTestPin3, OUTPUT_PIN)) { | |
179 if(gRTAudioVerbose) | |
180 cout << "Couldn't set direction on GPIO test pin 3\n"; | |
181 return -1; | |
182 } | |
183 if(gpio_set_value(kPruGPIOTestPin3, LOW)) { | |
184 if(gRTAudioVerbose) | |
185 cout << "Couldn't set value on GPIO test pin 3\n"; | |
186 return -1; | |
187 } | |
188 gpio_test_pin_enabled = true; | |
189 } | |
190 | |
191 if(include_led) { | |
192 // Turn off system function for LED3 so it can be reused by PRU | |
193 led_set_trigger(3, "none"); | |
194 led_enabled = true; | |
195 } | |
196 | |
197 gpio_enabled = true; | |
198 | |
199 return 0; | |
200 } | |
201 | |
202 // Clean up the GPIO at the end | |
203 void PRU::cleanupGPIO() | |
204 { | |
205 if(!gpio_enabled) | |
206 return; | |
207 if(spi_enabled) { | |
208 gpio_unexport(kPruGPIODACSyncPin); | |
209 gpio_unexport(kPruGPIOADCSyncPin); | |
210 } | |
211 if(gpio_test_pin_enabled) { | |
212 gpio_unexport(kPruGPIOTestPin); | |
213 gpio_unexport(kPruGPIOTestPin2); | |
214 gpio_unexport(kPruGPIOTestPin3); | |
215 } | |
216 if(led_enabled) { | |
217 // Set LED back to default eMMC status | |
218 // TODO: make it go back to its actual value before this program, | |
219 // rather than the system default | |
220 led_set_trigger(3, "mmc1"); | |
221 } | |
222 | |
223 gpio_enabled = gpio_test_pin_enabled = false; | |
224 } | |
225 | |
226 // Initialise and open the PRU | |
227 int PRU::initialise(int pru_num, int frames_per_buffer, bool xenomai_test_pin) | |
228 { | |
229 uint32_t *pruMem = 0; | |
230 | |
231 if(!gpio_enabled) { | |
232 rt_printf("initialise() called before GPIO enabled\n"); | |
233 return 1; | |
234 } | |
235 | |
236 pru_number = pru_num; | |
237 | |
238 /* Initialize structure used by prussdrv_pruintc_intc */ | |
239 /* PRUSS_INTC_INITDATA is found in pruss_intc_mapping.h */ | |
240 tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA; | |
241 | |
242 /* Allocate and initialize memory */ | |
243 prussdrv_init(); | |
244 if(prussdrv_open(PRU_EVTOUT_0)) { | |
245 rt_printf("Failed to open PRU driver\n"); | |
246 return 1; | |
247 } | |
248 | |
249 /* Map PRU's INTC */ | |
250 prussdrv_pruintc_init(&pruss_intc_initdata); | |
251 | |
252 spi_buffer_frames = frames_per_buffer; | |
253 audio_buffer_frames = spi_buffer_frames * 2; | |
254 | |
255 /* Map PRU memory to pointers */ | |
256 prussdrv_map_prumem (PRUSS0_SHARED_DATARAM, (void **)&pruMem); | |
257 pru_buffer_comm = (uint32_t *)&pruMem[PRU_MEM_COMM_OFFSET/sizeof(uint32_t)]; | |
258 pru_buffer_audio_dac = (int16_t *)&pruMem[PRU_MEM_MCASP_OFFSET/sizeof(uint32_t)]; | |
259 | |
260 /* ADC memory starts 2(ch)*2(buffers)*2(samples/spi)*bufsize samples later */ | |
261 pru_buffer_audio_adc = &pru_buffer_audio_dac[8 * spi_buffer_frames]; | |
262 | |
263 if(spi_enabled) { | |
264 prussdrv_map_prumem (pru_number == 0 ? PRUSS0_PRU0_DATARAM : PRUSS0_PRU1_DATARAM, (void **)&pruMem); | |
265 pru_buffer_spi_dac = (uint16_t *)&pruMem[PRU_MEM_DAC_OFFSET/sizeof(uint32_t)]; | |
266 | |
267 /* ADC memory starts after 8(ch)*2(buffers)*bufsize samples */ | |
268 pru_buffer_spi_adc = &pru_buffer_spi_dac[16 * spi_buffer_frames]; | |
269 } | |
270 else { | |
271 pru_buffer_spi_dac = pru_buffer_spi_adc = 0; | |
272 } | |
273 | |
274 /* Set up flags */ | |
275 pru_buffer_comm[PRU_SHOULD_STOP] = 0; | |
276 pru_buffer_comm[PRU_CURRENT_BUFFER] = 0; | |
277 pru_buffer_comm[PRU_BUFFER_FRAMES] = spi_buffer_frames; | |
278 pru_buffer_comm[PRU_SHOULD_SYNC] = 0; | |
279 pru_buffer_comm[PRU_SYNC_ADDRESS] = 0; | |
280 pru_buffer_comm[PRU_SYNC_PIN_MASK] = 0; | |
281 if(led_enabled) { | |
282 pru_buffer_comm[PRU_LED_ADDRESS] = USERLED3_GPIO_BASE; | |
283 pru_buffer_comm[PRU_LED_PIN_MASK] = USERLED3_PIN_MASK; | |
284 } | |
285 else { | |
286 pru_buffer_comm[PRU_LED_ADDRESS] = 0; | |
287 pru_buffer_comm[PRU_LED_PIN_MASK] = 0; | |
288 } | |
289 if(spi_enabled) { | |
290 pru_buffer_comm[PRU_USE_SPI] = 1; | |
291 } | |
292 else { | |
293 pru_buffer_comm[PRU_USE_SPI] = 0; | |
294 } | |
295 | |
296 /* Clear ADC and DAC memory */ | |
297 if(spi_enabled) { | |
298 for(int i = 0; i < PRU_MEM_DAC_LENGTH / 2; i++) | |
299 pru_buffer_spi_dac[i] = 0; | |
300 } | |
301 for(int i = 0; i < PRU_MEM_MCASP_LENGTH / 2; i++) | |
302 pru_buffer_audio_dac[i] = 0; | |
303 | |
304 /* If using GPIO test pin for Xenomai (for debugging), initialise the pointer now */ | |
305 if(xenomai_test_pin && xenomai_gpio_fd < 0) { | |
306 xenomai_gpio_fd = open("/dev/mem", O_RDWR); | |
307 if(xenomai_gpio_fd < 0) | |
308 rt_printf("Unable to open /dev/mem for GPIO test pin\n"); | |
309 else { | |
310 xenomai_gpio = (uint32_t *)mmap(0, GPIO_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, xenomai_gpio_fd, TEST_PIN_GPIO_BASE); | |
311 if(xenomai_gpio == MAP_FAILED) { | |
312 rt_printf("Unable to map GPIO address for test pin\n"); | |
313 xenomai_gpio = 0; | |
314 close(xenomai_gpio_fd); | |
315 xenomai_gpio_fd = -1; | |
316 } | |
317 } | |
318 } | |
319 | |
320 return 0; | |
321 } | |
322 | |
323 // Run the code image in the specified file | |
324 int PRU::start(char * const filename) | |
325 { | |
326 /* Clear any old interrupt */ | |
327 prussdrv_pru_clear_event(pru_number == 0 ? PRU0_ARM_INTERRUPT : PRU1_ARM_INTERRUPT); | |
328 | |
329 /* Load and execute binary on PRU */ | |
330 if(prussdrv_exec_program(pru_number, filename)) { | |
331 rt_printf("Failed to execute PRU code from %s\n", filename); | |
332 return 1; | |
333 } | |
334 | |
335 running = true; | |
336 return 0; | |
337 } | |
338 | |
339 // Main loop to read and write data from/to PRU | |
340 void PRU::loop() | |
341 { | |
342 // Polling interval is 1/4 of the period | |
343 RTIME sleepTime = PRU_SAMPLE_INTERVAL_NS * spi_buffer_frames / 4; | |
344 float *audioInBuffer, *audioOutBuffer; | |
345 | |
346 audioInBuffer = (float *)malloc(2 * audio_buffer_frames * sizeof(float)); | |
347 audioOutBuffer = (float *)malloc(2 * audio_buffer_frames * sizeof(float)); | |
348 | |
349 if(audioInBuffer == 0 || audioOutBuffer == 0) { | |
350 rt_printf("Error: couldn't allocated audio buffers\n"); | |
351 return; | |
352 } | |
353 | |
354 while(!gShouldStop) { | |
355 // Wait for PRU to move to buffer 1 | |
356 while(pru_buffer_comm[PRU_CURRENT_BUFFER] == 0 && !gShouldStop) { | |
357 rt_task_sleep(sleepTime); | |
358 } | |
359 if(gShouldStop) | |
360 break; | |
361 | |
362 if(xenomai_gpio != 0) { | |
363 // Set the test pin high | |
364 xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN_MASK; | |
365 } | |
366 | |
367 // Render from/to buffer 0 | |
368 | |
369 // Convert short (16-bit) samples to float | |
370 for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++) | |
371 audioInBuffer[n] = (float)pru_buffer_audio_adc[n] / 32768.0; | |
372 | |
373 if(spi_enabled) | |
374 render(spi_buffer_frames, audio_buffer_frames, audioInBuffer, audioOutBuffer, | |
375 pru_buffer_spi_adc, pru_buffer_spi_dac); | |
376 else | |
377 render(0, audio_buffer_frames, audioInBuffer, audioOutBuffer, 0, 0); | |
378 | |
379 // Convert float back to short | |
380 for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++) { | |
381 int out = audioOutBuffer[n] * 32768.0; | |
382 if(out < -32768) out = -32768; | |
383 else if(out > 32767) out = 32767; | |
384 pru_buffer_audio_dac[n] = (int16_t)out; | |
385 } | |
386 | |
387 if(xenomai_gpio != 0) { | |
388 // Set the test pin high | |
389 xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN_MASK; | |
390 } | |
391 | |
392 // Wait for PRU to move to buffer 0 | |
393 while(pru_buffer_comm[PRU_CURRENT_BUFFER] != 0 && !gShouldStop) { | |
394 rt_task_sleep(sleepTime); | |
395 } | |
396 | |
397 if(gShouldStop) | |
398 break; | |
399 | |
400 if(xenomai_gpio != 0) { | |
401 // Set the test pin high | |
402 xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN_MASK; | |
403 } | |
404 | |
405 // Render from/to buffer 1 | |
406 | |
407 // Convert short (16-bit) samples to float | |
408 for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++) | |
409 audioInBuffer[n] = (float)pru_buffer_audio_adc[n + audio_buffer_frames * 2] / 32768.0; | |
410 | |
411 if(spi_enabled) | |
412 render(spi_buffer_frames, audio_buffer_frames, audioInBuffer, audioOutBuffer, | |
413 &pru_buffer_spi_adc[spi_buffer_frames * 8], &pru_buffer_spi_dac[spi_buffer_frames * 8]); | |
414 else | |
415 render(0, audio_buffer_frames, audioInBuffer, audioOutBuffer, 0, 0); | |
416 | |
417 // Convert float back to short | |
418 for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++) { | |
419 int out = audioOutBuffer[n] * 32768.0; | |
420 if(out < -32768) out = -32768; | |
421 else if(out > 32767) out = 32767; | |
422 pru_buffer_audio_dac[n + audio_buffer_frames * 2] = (int16_t)out; | |
423 } | |
424 | |
425 if(xenomai_gpio != 0) { | |
426 // Set the test pin high | |
427 xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN_MASK; | |
428 } | |
429 } | |
430 | |
431 // Tell PRU to stop | |
432 pru_buffer_comm[PRU_SHOULD_STOP] = 1; | |
433 | |
434 free(audioInBuffer); | |
435 free(audioOutBuffer); | |
436 } | |
437 | |
438 // Wait for an interrupt from the PRU indicate it is finished | |
439 void PRU::waitForFinish() | |
440 { | |
441 if(!running) | |
442 return; | |
443 prussdrv_pru_wait_event (PRU_EVTOUT_0); | |
444 prussdrv_pru_clear_event(pru_number == 0 ? PRU0_ARM_INTERRUPT : PRU1_ARM_INTERRUPT); | |
445 } | |
446 | |
447 // Turn off the PRU when done | |
448 void PRU::disable() | |
449 { | |
450 /* Disable PRU and close memory mapping*/ | |
451 prussdrv_pru_disable(pru_number); | |
452 prussdrv_exit(); | |
453 running = false; | |
454 } | |
455 | |
456 // Debugging | |
457 void PRU::setGPIOTestPin() | |
458 { | |
459 if(!xenomai_gpio) | |
460 return; | |
461 xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN2_MASK; | |
462 } | |
463 | |
464 void PRU::clearGPIOTestPin() | |
465 { | |
466 if(!xenomai_gpio) | |
467 return; | |
468 xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN2_MASK; | |
469 } |