andrewm@0
|
1 /*
|
andrewm@0
|
2 * PRU.cpp
|
andrewm@0
|
3 *
|
andrewm@0
|
4 * Code for communicating with the Programmable Realtime Unit (PRU)
|
andrewm@0
|
5 * on the BeagleBone AM335x series processors. The PRU loads and runs
|
andrewm@0
|
6 * a separate code image compiled from an assembly file. Here it is
|
andrewm@0
|
7 * used to handle audio and SPI ADC/DAC data.
|
andrewm@0
|
8 *
|
andrewm@0
|
9 * This code is specific to the PRU code in the assembly file; for example,
|
andrewm@0
|
10 * it uses certain GPIO resources that correspond to that image.
|
andrewm@0
|
11 *
|
andrewm@0
|
12 * Created on: May 27, 2014
|
andrewm@0
|
13 * Author: andrewm
|
andrewm@0
|
14 */
|
andrewm@0
|
15
|
andrewm@0
|
16 #include "../include/PRU.h"
|
andrewm@0
|
17 #include "../include/prussdrv.h"
|
andrewm@0
|
18 #include "../include/pruss_intc_mapping.h"
|
giuliomoro@19
|
19 #include "../include/digital_gpio_mapping.h"
|
andrewm@0
|
20 #include "../include/GPIOcontrol.h"
|
giuliomoro@301
|
21 #include "../include/Bela.h"
|
andrewm@15
|
22 #include "../include/pru_rtaudio_bin.h"
|
andrewm@0
|
23
|
andrewm@0
|
24 #include <iostream>
|
andrewm@0
|
25 #include <stdlib.h>
|
andrewm@0
|
26 #include <cstdio>
|
andrewm@0
|
27 #include <cerrno>
|
andrewm@0
|
28 #include <fcntl.h>
|
andrewm@0
|
29 #include <sys/mman.h>
|
giuliomoro@16
|
30 #include <unistd.h>
|
andrewm@0
|
31
|
andrewm@0
|
32 // Xenomai-specific includes
|
andrewm@0
|
33 #include <sys/mman.h>
|
andrewm@0
|
34 #include <native/task.h>
|
andrewm@0
|
35 #include <native/timer.h>
|
andrewm@0
|
36 #include <rtdk.h>
|
andrewm@0
|
37
|
andrewm@0
|
38 using namespace std;
|
andrewm@0
|
39
|
andrewm@318
|
40 // Select whether to use NEON-based sample conversion
|
andrewm@318
|
41 // (this will probably go away in a future commit once its performance
|
andrewm@318
|
42 // is verified over extended use)
|
andrewm@318
|
43 #undef USE_NEON_FORMAT_CONVERSION
|
andrewm@318
|
44
|
andrewm@268
|
45 // PRU memory: PRU0 and PRU1 RAM are 8kB (0x2000) long each
|
andrewm@268
|
46 // PRU-SHARED RAM is 12kB (0x3000) long
|
andrewm@268
|
47
|
andrewm@0
|
48 #define PRU_MEM_MCASP_OFFSET 0x2000 // Offset within PRU-SHARED RAM
|
andrewm@253
|
49 #define PRU_MEM_MCASP_LENGTH 0x1000 // Length of McASP memory, in bytes
|
andrewm@0
|
50 #define PRU_MEM_DAC_OFFSET 0x0 // Offset within PRU0 RAM
|
andrewm@0
|
51 #define PRU_MEM_DAC_LENGTH 0x2000 // Length of ADC+DAC memory, in bytes
|
andrewm@0
|
52 #define PRU_MEM_COMM_OFFSET 0x0 // Offset within PRU-SHARED RAM
|
giuliomoro@19
|
53 #define PRU_MEM_DIGITAL_OFFSET 0x1000 //Offset within PRU-SHARED RAM
|
giuliomoro@19
|
54 #define MEM_DIGITAL_BUFFER1_OFFSET 0x400 //Start pointer to DIGITAL_BUFFER1, which is 256 words.
|
giuliomoro@16
|
55 // 256 is the maximum number of frames allowed
|
andrewm@280
|
56
|
andrewm@280
|
57 // Offsets within CPU <-> PRU communication memory (4 byte slots)
|
andrewm@0
|
58 #define PRU_SHOULD_STOP 0
|
andrewm@0
|
59 #define PRU_CURRENT_BUFFER 1
|
andrewm@0
|
60 #define PRU_BUFFER_FRAMES 2
|
andrewm@0
|
61 #define PRU_SHOULD_SYNC 3
|
andrewm@0
|
62 #define PRU_SYNC_ADDRESS 4
|
andrewm@0
|
63 #define PRU_SYNC_PIN_MASK 5
|
andrewm@253
|
64 #define PRU_LED_ADDRESS 6
|
andrewm@253
|
65 #define PRU_LED_PIN_MASK 7
|
andrewm@253
|
66 #define PRU_FRAME_COUNT 8
|
andrewm@253
|
67 #define PRU_USE_SPI 9
|
andrewm@12
|
68 #define PRU_SPI_NUM_CHANNELS 10
|
andrewm@253
|
69 #define PRU_USE_DIGITAL 11
|
andrewm@253
|
70 #define PRU_PRU_NUMBER 12
|
andrewm@280
|
71 #define PRU_MUX_CONFIG 13
|
andrewm@303
|
72 #define PRU_MUX_END_CHANNEL 14
|
giuliomoro@16
|
73
|
andrewm@280
|
74 short int digitalPins[NUM_DIGITALS] = {
|
giuliomoro@16
|
75 GPIO_NO_BIT_0,
|
giuliomoro@16
|
76 GPIO_NO_BIT_1,
|
giuliomoro@16
|
77 GPIO_NO_BIT_2,
|
giuliomoro@16
|
78 GPIO_NO_BIT_3,
|
giuliomoro@16
|
79 GPIO_NO_BIT_4,
|
giuliomoro@16
|
80 GPIO_NO_BIT_5,
|
giuliomoro@16
|
81 GPIO_NO_BIT_6,
|
giuliomoro@16
|
82 GPIO_NO_BIT_7,
|
giuliomoro@16
|
83 GPIO_NO_BIT_8,
|
giuliomoro@16
|
84 GPIO_NO_BIT_9,
|
giuliomoro@16
|
85 GPIO_NO_BIT_10,
|
giuliomoro@16
|
86 GPIO_NO_BIT_11,
|
giuliomoro@16
|
87 GPIO_NO_BIT_12,
|
giuliomoro@16
|
88 GPIO_NO_BIT_13,
|
giuliomoro@16
|
89 GPIO_NO_BIT_14,
|
giuliomoro@16
|
90 GPIO_NO_BIT_15,
|
giuliomoro@16
|
91 };
|
andrewm@0
|
92
|
andrewm@12
|
93 #define PRU_SAMPLE_INTERVAL_NS 11338 // 88200Hz per SPI sample = 11.338us
|
andrewm@0
|
94
|
andrewm@0
|
95 #define GPIO0_ADDRESS 0x44E07000
|
andrewm@0
|
96 #define GPIO1_ADDRESS 0x4804C000
|
andrewm@0
|
97 #define GPIO_SIZE 0x198
|
andrewm@0
|
98 #define GPIO_CLEARDATAOUT (0x190 / 4)
|
andrewm@0
|
99 #define GPIO_SETDATAOUT (0x194 / 4)
|
andrewm@0
|
100
|
andrewm@0
|
101 #define TEST_PIN_GPIO_BASE GPIO0_ADDRESS // Use GPIO0(31) for debugging
|
andrewm@0
|
102 #define TEST_PIN_MASK (1 << 31)
|
andrewm@0
|
103 #define TEST_PIN2_MASK (1 << 26)
|
andrewm@0
|
104
|
andrewm@0
|
105 #define USERLED3_GPIO_BASE GPIO1_ADDRESS // GPIO1(24) is user LED 3
|
andrewm@0
|
106 #define USERLED3_PIN_MASK (1 << 24)
|
andrewm@0
|
107
|
andrewm@0
|
108 const unsigned int PRU::kPruGPIODACSyncPin = 5; // GPIO0(5); P9-17
|
andrewm@0
|
109 const unsigned int PRU::kPruGPIOADCSyncPin = 48; // GPIO1(16); P9-15
|
andrewm@0
|
110
|
andrewm@0
|
111 const unsigned int PRU::kPruGPIOTestPin = 60; // GPIO1(28); P9-12
|
andrewm@0
|
112 const unsigned int PRU::kPruGPIOTestPin2 = 31; // GPIO0(31); P9-13
|
andrewm@0
|
113 const unsigned int PRU::kPruGPIOTestPin3 = 26; // GPIO0(26); P8-14
|
andrewm@0
|
114
|
giuliomoro@231
|
115 extern int gShouldStop;
|
andrewm@0
|
116 extern int gRTAudioVerbose;
|
andrewm@0
|
117
|
andrewm@318
|
118 // These four functions are written in assembly in FormatConvert.S
|
andrewm@318
|
119 extern "C" {
|
andrewm@318
|
120 void int16_to_float_audio(int numSamples, int16_t *inBuffer, float *outBuffer);
|
andrewm@318
|
121 void int16_to_float_analog(int numSamples, uint16_t *inBuffer, float *outBuffer);
|
andrewm@318
|
122 void float_to_int16_audio(int numSamples, float *inBuffer, int16_t *outBuffer);
|
andrewm@318
|
123 void float_to_int16_analog(int numSamples, float *inBuffer, uint16_t *outBuffer);
|
andrewm@318
|
124 }
|
andrewm@318
|
125
|
andrewm@0
|
126 // Constructor: specify a PRU number (0 or 1)
|
andrewm@307
|
127 PRU::PRU(InternalBelaContext *input_context)
|
andrewm@45
|
128 : context(input_context), pru_number(0), running(false), analog_enabled(false),
|
andrewm@45
|
129 digital_enabled(false), gpio_enabled(false), led_enabled(false),
|
andrewm@303
|
130 mux_channels(0),
|
andrewm@45
|
131 gpio_test_pin_enabled(false),
|
andrewm@45
|
132 pru_buffer_comm(0), pru_buffer_spi_dac(0), pru_buffer_spi_adc(0),
|
andrewm@45
|
133 pru_buffer_digital(0), pru_buffer_audio_dac(0), pru_buffer_audio_adc(0),
|
andrewm@45
|
134 xenomai_gpio_fd(-1), xenomai_gpio(0)
|
andrewm@0
|
135 {
|
andrewm@0
|
136
|
andrewm@0
|
137 }
|
andrewm@0
|
138
|
andrewm@0
|
139 // Destructor
|
andrewm@0
|
140 PRU::~PRU()
|
andrewm@0
|
141 {
|
andrewm@0
|
142 if(running)
|
andrewm@0
|
143 disable();
|
andrewm@0
|
144 if(gpio_enabled)
|
andrewm@0
|
145 cleanupGPIO();
|
andrewm@0
|
146 if(xenomai_gpio_fd >= 0)
|
andrewm@0
|
147 close(xenomai_gpio_fd);
|
andrewm@0
|
148 }
|
andrewm@0
|
149
|
andrewm@0
|
150 // Prepare the GPIO pins needed for the PRU
|
andrewm@0
|
151 // If include_test_pin is set, the GPIO output
|
andrewm@0
|
152 // is also prepared for an output which can be
|
andrewm@0
|
153 // viewed on a scope. If include_led is set,
|
andrewm@0
|
154 // user LED 3 on the BBB is taken over by the PRU
|
andrewm@0
|
155 // to indicate activity
|
andrewm@45
|
156 int PRU::prepareGPIO(int include_test_pin, int include_led)
|
andrewm@0
|
157 {
|
andrewm@45
|
158 if(context->analogFrames != 0) {
|
andrewm@0
|
159 // Prepare DAC CS/ pin: output, high to begin
|
andrewm@0
|
160 if(gpio_export(kPruGPIODACSyncPin)) {
|
andrewm@0
|
161 if(gRTAudioVerbose)
|
andrewm@0
|
162 cout << "Warning: couldn't export DAC sync pin\n";
|
andrewm@0
|
163 }
|
andrewm@0
|
164 if(gpio_set_dir(kPruGPIODACSyncPin, OUTPUT_PIN)) {
|
andrewm@0
|
165 if(gRTAudioVerbose)
|
andrewm@0
|
166 cout << "Couldn't set direction on DAC sync pin\n";
|
andrewm@0
|
167 return -1;
|
andrewm@0
|
168 }
|
andrewm@0
|
169 if(gpio_set_value(kPruGPIODACSyncPin, HIGH)) {
|
andrewm@0
|
170 if(gRTAudioVerbose)
|
andrewm@0
|
171 cout << "Couldn't set value on DAC sync pin\n";
|
andrewm@0
|
172 return -1;
|
andrewm@0
|
173 }
|
andrewm@0
|
174
|
andrewm@0
|
175 // Prepare ADC CS/ pin: output, high to begin
|
andrewm@0
|
176 if(gpio_export(kPruGPIOADCSyncPin)) {
|
andrewm@0
|
177 if(gRTAudioVerbose)
|
andrewm@0
|
178 cout << "Warning: couldn't export ADC sync pin\n";
|
andrewm@0
|
179 }
|
andrewm@0
|
180 if(gpio_set_dir(kPruGPIOADCSyncPin, OUTPUT_PIN)) {
|
andrewm@0
|
181 if(gRTAudioVerbose)
|
andrewm@0
|
182 cout << "Couldn't set direction on ADC sync pin\n";
|
andrewm@0
|
183 return -1;
|
andrewm@0
|
184 }
|
andrewm@0
|
185 if(gpio_set_value(kPruGPIOADCSyncPin, HIGH)) {
|
andrewm@0
|
186 if(gRTAudioVerbose)
|
andrewm@0
|
187 cout << "Couldn't set value on ADC sync pin\n";
|
andrewm@0
|
188 return -1;
|
andrewm@0
|
189 }
|
andrewm@0
|
190
|
andrewm@45
|
191 analog_enabled = true;
|
andrewm@0
|
192 }
|
andrewm@0
|
193
|
andrewm@45
|
194 if(context->digitalFrames != 0){
|
andrewm@45
|
195 for(unsigned int i = 0; i < context->digitalChannels; i++){
|
giuliomoro@19
|
196 if(gpio_export(digitalPins[i])) {
|
giuliomoro@16
|
197 if(gRTAudioVerbose)
|
giuliomoro@38
|
198 cerr << "Warning: couldn't export digital GPIO pin " << digitalPins[i] << "\n"; // this is left as a warning because if the pin has been exported by somebody else, can still be used
|
giuliomoro@16
|
199 }
|
giuliomoro@38
|
200 if(gpio_set_dir(digitalPins[i], INPUT_PIN)) {
|
giuliomoro@16
|
201 if(gRTAudioVerbose)
|
giuliomoro@38
|
202 cerr << "Error: Couldn't set direction on digital GPIO pin " << digitalPins[i] << "\n";
|
giuliomoro@16
|
203 return -1;
|
giuliomoro@16
|
204 }
|
giuliomoro@16
|
205 }
|
andrewm@45
|
206 digital_enabled = true;
|
giuliomoro@16
|
207 }
|
giuliomoro@16
|
208
|
andrewm@0
|
209 if(include_test_pin) {
|
andrewm@0
|
210 // Prepare GPIO test output (for debugging), low to begin
|
andrewm@0
|
211 if(gpio_export(kPruGPIOTestPin)) {
|
andrewm@0
|
212 if(gRTAudioVerbose)
|
andrewm@0
|
213 cout << "Warning: couldn't export GPIO test pin\n";
|
andrewm@0
|
214 }
|
andrewm@0
|
215 if(gpio_set_dir(kPruGPIOTestPin, OUTPUT_PIN)) {
|
andrewm@0
|
216 if(gRTAudioVerbose)
|
andrewm@0
|
217 cout << "Couldn't set direction on GPIO test pin\n";
|
andrewm@0
|
218 return -1;
|
andrewm@0
|
219 }
|
andrewm@0
|
220 if(gpio_set_value(kPruGPIOTestPin, LOW)) {
|
andrewm@0
|
221 if(gRTAudioVerbose)
|
andrewm@0
|
222 cout << "Couldn't set value on GPIO test pin\n";
|
andrewm@0
|
223 return -1;
|
andrewm@0
|
224 }
|
andrewm@0
|
225
|
andrewm@0
|
226 if(gpio_export(kPruGPIOTestPin2)) {
|
andrewm@0
|
227 if(gRTAudioVerbose)
|
andrewm@0
|
228 cout << "Warning: couldn't export GPIO test pin 2\n";
|
andrewm@0
|
229 }
|
andrewm@0
|
230 if(gpio_set_dir(kPruGPIOTestPin2, OUTPUT_PIN)) {
|
andrewm@0
|
231 if(gRTAudioVerbose)
|
andrewm@0
|
232 cout << "Couldn't set direction on GPIO test pin 2\n";
|
andrewm@0
|
233 return -1;
|
andrewm@0
|
234 }
|
andrewm@0
|
235 if(gpio_set_value(kPruGPIOTestPin2, LOW)) {
|
andrewm@0
|
236 if(gRTAudioVerbose)
|
andrewm@0
|
237 cout << "Couldn't set value on GPIO test pin 2\n";
|
andrewm@0
|
238 return -1;
|
andrewm@0
|
239 }
|
andrewm@0
|
240
|
andrewm@0
|
241 if(gpio_export(kPruGPIOTestPin3)) {
|
andrewm@0
|
242 if(gRTAudioVerbose)
|
andrewm@0
|
243 cout << "Warning: couldn't export GPIO test pin 3\n";
|
andrewm@0
|
244 }
|
andrewm@0
|
245 if(gpio_set_dir(kPruGPIOTestPin3, OUTPUT_PIN)) {
|
andrewm@0
|
246 if(gRTAudioVerbose)
|
andrewm@0
|
247 cout << "Couldn't set direction on GPIO test pin 3\n";
|
andrewm@0
|
248 return -1;
|
andrewm@0
|
249 }
|
andrewm@0
|
250 if(gpio_set_value(kPruGPIOTestPin3, LOW)) {
|
andrewm@0
|
251 if(gRTAudioVerbose)
|
andrewm@0
|
252 cout << "Couldn't set value on GPIO test pin 3\n";
|
andrewm@0
|
253 return -1;
|
andrewm@0
|
254 }
|
andrewm@0
|
255 gpio_test_pin_enabled = true;
|
andrewm@0
|
256 }
|
andrewm@0
|
257
|
andrewm@0
|
258 if(include_led) {
|
andrewm@0
|
259 // Turn off system function for LED3 so it can be reused by PRU
|
andrewm@0
|
260 led_set_trigger(3, "none");
|
andrewm@0
|
261 led_enabled = true;
|
andrewm@0
|
262 }
|
andrewm@0
|
263
|
andrewm@0
|
264 gpio_enabled = true;
|
andrewm@0
|
265
|
andrewm@0
|
266 return 0;
|
andrewm@0
|
267 }
|
andrewm@0
|
268
|
andrewm@0
|
269 // Clean up the GPIO at the end
|
andrewm@0
|
270 void PRU::cleanupGPIO()
|
andrewm@0
|
271 {
|
andrewm@0
|
272 if(!gpio_enabled)
|
andrewm@0
|
273 return;
|
andrewm@45
|
274 if(analog_enabled) {
|
andrewm@0
|
275 gpio_unexport(kPruGPIODACSyncPin);
|
andrewm@0
|
276 gpio_unexport(kPruGPIOADCSyncPin);
|
andrewm@0
|
277 }
|
giuliomoro@19
|
278 if(digital_enabled){
|
andrewm@45
|
279 for(unsigned int i = 0; i < context->digitalChannels; i++){
|
giuliomoro@19
|
280 gpio_unexport(digitalPins[i]);
|
giuliomoro@16
|
281 }
|
giuliomoro@16
|
282 }
|
andrewm@0
|
283 if(gpio_test_pin_enabled) {
|
andrewm@0
|
284 gpio_unexport(kPruGPIOTestPin);
|
andrewm@0
|
285 gpio_unexport(kPruGPIOTestPin2);
|
andrewm@0
|
286 gpio_unexport(kPruGPIOTestPin3);
|
andrewm@0
|
287 }
|
andrewm@0
|
288 if(led_enabled) {
|
andrewm@0
|
289 // Set LED back to default eMMC status
|
andrewm@0
|
290 // TODO: make it go back to its actual value before this program,
|
andrewm@0
|
291 // rather than the system default
|
andrewm@0
|
292 led_set_trigger(3, "mmc1");
|
andrewm@0
|
293 }
|
andrewm@0
|
294 gpio_enabled = gpio_test_pin_enabled = false;
|
andrewm@0
|
295 }
|
andrewm@0
|
296
|
andrewm@0
|
297 // Initialise and open the PRU
|
andrewm@280
|
298 int PRU::initialise(int pru_num, int frames_per_buffer, int spi_channels, int mux_channels, bool xenomai_test_pin)
|
andrewm@0
|
299 {
|
andrewm@0
|
300 uint32_t *pruMem = 0;
|
andrewm@0
|
301
|
andrewm@0
|
302 if(!gpio_enabled) {
|
andrewm@0
|
303 rt_printf("initialise() called before GPIO enabled\n");
|
andrewm@0
|
304 return 1;
|
andrewm@0
|
305 }
|
andrewm@0
|
306
|
andrewm@0
|
307 pru_number = pru_num;
|
andrewm@303
|
308 this->mux_channels = mux_channels;
|
andrewm@0
|
309
|
andrewm@0
|
310 /* Initialize structure used by prussdrv_pruintc_intc */
|
andrewm@0
|
311 /* PRUSS_INTC_INITDATA is found in pruss_intc_mapping.h */
|
andrewm@0
|
312 tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA;
|
andrewm@0
|
313
|
andrewm@0
|
314 /* Allocate and initialize memory */
|
andrewm@0
|
315 prussdrv_init();
|
andrewm@45
|
316 if(prussdrv_open(PRU_EVTOUT_0)) {
|
andrewm@0
|
317 rt_printf("Failed to open PRU driver\n");
|
andrewm@0
|
318 return 1;
|
andrewm@0
|
319 }
|
andrewm@0
|
320
|
andrewm@0
|
321 /* Map PRU's INTC */
|
andrewm@0
|
322 prussdrv_pruintc_init(&pruss_intc_initdata);
|
andrewm@0
|
323
|
andrewm@0
|
324 /* Map PRU memory to pointers */
|
andrewm@0
|
325 prussdrv_map_prumem (PRUSS0_SHARED_DATARAM, (void **)&pruMem);
|
andrewm@0
|
326 pru_buffer_comm = (uint32_t *)&pruMem[PRU_MEM_COMM_OFFSET/sizeof(uint32_t)];
|
andrewm@0
|
327 pru_buffer_audio_dac = (int16_t *)&pruMem[PRU_MEM_MCASP_OFFSET/sizeof(uint32_t)];
|
andrewm@0
|
328
|
andrewm@12
|
329 /* ADC memory starts 2(ch)*2(buffers)*bufsize samples later */
|
andrewm@45
|
330 pru_buffer_audio_adc = &pru_buffer_audio_dac[4 * context->audioFrames];
|
andrewm@0
|
331
|
andrewm@45
|
332 if(analog_enabled) {
|
andrewm@0
|
333 prussdrv_map_prumem (pru_number == 0 ? PRUSS0_PRU0_DATARAM : PRUSS0_PRU1_DATARAM, (void **)&pruMem);
|
andrewm@0
|
334 pru_buffer_spi_dac = (uint16_t *)&pruMem[PRU_MEM_DAC_OFFSET/sizeof(uint32_t)];
|
andrewm@0
|
335
|
andrewm@12
|
336 /* ADC memory starts after N(ch)*2(buffers)*bufsize samples */
|
giuliomoro@528
|
337 pru_buffer_spi_adc = &pru_buffer_spi_dac[2 * context->analogInChannels * context->analogFrames];
|
andrewm@0
|
338 }
|
andrewm@0
|
339 else {
|
andrewm@0
|
340 pru_buffer_spi_dac = pru_buffer_spi_adc = 0;
|
andrewm@0
|
341 }
|
andrewm@0
|
342
|
giuliomoro@19
|
343 if(digital_enabled) {
|
giuliomoro@16
|
344 prussdrv_map_prumem (PRUSS0_SHARED_DATARAM, (void **)&pruMem);
|
giuliomoro@19
|
345 pru_buffer_digital = (uint32_t *)&pruMem[PRU_MEM_DIGITAL_OFFSET/sizeof(uint32_t)];
|
giuliomoro@16
|
346 }
|
giuliomoro@16
|
347 else {
|
giuliomoro@19
|
348 pru_buffer_digital = 0;
|
giuliomoro@16
|
349 }
|
andrewm@45
|
350
|
andrewm@0
|
351 /* Set up flags */
|
andrewm@0
|
352 pru_buffer_comm[PRU_SHOULD_STOP] = 0;
|
andrewm@0
|
353 pru_buffer_comm[PRU_CURRENT_BUFFER] = 0;
|
andrewm@45
|
354 pru_buffer_comm[PRU_BUFFER_FRAMES] = context->analogFrames;
|
andrewm@0
|
355 pru_buffer_comm[PRU_SHOULD_SYNC] = 0;
|
andrewm@0
|
356 pru_buffer_comm[PRU_SYNC_ADDRESS] = 0;
|
andrewm@0
|
357 pru_buffer_comm[PRU_SYNC_PIN_MASK] = 0;
|
andrewm@253
|
358 pru_buffer_comm[PRU_PRU_NUMBER] = pru_number;
|
andrewm@280
|
359
|
andrewm@280
|
360 if(mux_channels == 2)
|
andrewm@280
|
361 pru_buffer_comm[PRU_MUX_CONFIG] = 1;
|
andrewm@280
|
362 else if(mux_channels == 4)
|
andrewm@280
|
363 pru_buffer_comm[PRU_MUX_CONFIG] = 2;
|
andrewm@280
|
364 else if(mux_channels == 8)
|
andrewm@280
|
365 pru_buffer_comm[PRU_MUX_CONFIG] = 3;
|
andrewm@280
|
366 else
|
andrewm@280
|
367 pru_buffer_comm[PRU_MUX_CONFIG] = 0;
|
andrewm@280
|
368
|
andrewm@0
|
369 if(led_enabled) {
|
andrewm@0
|
370 pru_buffer_comm[PRU_LED_ADDRESS] = USERLED3_GPIO_BASE;
|
andrewm@0
|
371 pru_buffer_comm[PRU_LED_PIN_MASK] = USERLED3_PIN_MASK;
|
andrewm@0
|
372 }
|
andrewm@0
|
373 else {
|
andrewm@0
|
374 pru_buffer_comm[PRU_LED_ADDRESS] = 0;
|
andrewm@0
|
375 pru_buffer_comm[PRU_LED_PIN_MASK] = 0;
|
andrewm@0
|
376 }
|
andrewm@45
|
377 if(analog_enabled) {
|
andrewm@0
|
378 pru_buffer_comm[PRU_USE_SPI] = 1;
|
giuliomoro@528
|
379 if(context->analogInChannels != context->analogOutChannels){
|
giuliomoro@528
|
380 printf("Error: TODO: a different number of channels for inputs and outputs is not yet supported\n");
|
giuliomoro@528
|
381 return 1;
|
giuliomoro@528
|
382 }
|
giuliomoro@528
|
383 unsigned int analogChannels = context->analogInChannels;
|
giuliomoro@528
|
384 pru_buffer_comm[PRU_SPI_NUM_CHANNELS] = analogChannels;
|
andrewm@0
|
385 }
|
andrewm@0
|
386 else {
|
andrewm@0
|
387 pru_buffer_comm[PRU_USE_SPI] = 0;
|
andrewm@12
|
388 pru_buffer_comm[PRU_SPI_NUM_CHANNELS] = 0;
|
andrewm@0
|
389 }
|
giuliomoro@19
|
390 if(digital_enabled) {
|
giuliomoro@38
|
391 pru_buffer_comm[PRU_USE_DIGITAL] = 1;
|
giuliomoro@38
|
392 //TODO: add mask
|
giuliomoro@16
|
393 }
|
giuliomoro@16
|
394 else {
|
giuliomoro@38
|
395 pru_buffer_comm[PRU_USE_DIGITAL] = 0;
|
giuliomoro@38
|
396
|
giuliomoro@16
|
397 }
|
andrewm@0
|
398
|
giuliomoro@38
|
399 /* Clear ADC and DAC memory.*/
|
giuliomoro@38
|
400 //TODO: this initialisation should only address the memory effectively used by these buffers, i.e.:depend on the number of frames
|
giuliomoro@38
|
401 // (otherwise might cause issues if we move memory locations later on)
|
andrewm@45
|
402 if(analog_enabled) {
|
andrewm@0
|
403 for(int i = 0; i < PRU_MEM_DAC_LENGTH / 2; i++)
|
andrewm@0
|
404 pru_buffer_spi_dac[i] = 0;
|
andrewm@0
|
405 }
|
andrewm@320
|
406 if(digital_enabled){
|
andrewm@320
|
407 for(int i = 0; i < PRU_MEM_DIGITAL_OFFSET*2; i++)
|
andrewm@320
|
408 pru_buffer_digital[i] = 0x0000ffff; // set to all inputs, to avoid unexpected spikes
|
andrewm@320
|
409 }
|
andrewm@0
|
410 for(int i = 0; i < PRU_MEM_MCASP_LENGTH / 2; i++)
|
andrewm@0
|
411 pru_buffer_audio_dac[i] = 0;
|
andrewm@45
|
412
|
andrewm@0
|
413 /* If using GPIO test pin for Xenomai (for debugging), initialise the pointer now */
|
andrewm@0
|
414 if(xenomai_test_pin && xenomai_gpio_fd < 0) {
|
andrewm@0
|
415 xenomai_gpio_fd = open("/dev/mem", O_RDWR);
|
andrewm@0
|
416 if(xenomai_gpio_fd < 0)
|
andrewm@0
|
417 rt_printf("Unable to open /dev/mem for GPIO test pin\n");
|
andrewm@0
|
418 else {
|
andrewm@0
|
419 xenomai_gpio = (uint32_t *)mmap(0, GPIO_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, xenomai_gpio_fd, TEST_PIN_GPIO_BASE);
|
andrewm@0
|
420 if(xenomai_gpio == MAP_FAILED) {
|
andrewm@0
|
421 rt_printf("Unable to map GPIO address for test pin\n");
|
andrewm@0
|
422 xenomai_gpio = 0;
|
andrewm@0
|
423 close(xenomai_gpio_fd);
|
andrewm@0
|
424 xenomai_gpio_fd = -1;
|
andrewm@0
|
425 }
|
andrewm@0
|
426 }
|
andrewm@0
|
427 }
|
andrewm@0
|
428
|
andrewm@81
|
429 // Allocate audio buffers
|
andrewm@318
|
430 #ifdef USE_NEON_FORMAT_CONVERSION
|
andrewm@318
|
431 if(posix_memalign((void **)&context->audioIn, 16, 2 * context->audioFrames * sizeof(float))) {
|
andrewm@318
|
432 printf("Error allocating audio input buffer\n");
|
andrewm@318
|
433 return 1;
|
andrewm@318
|
434 }
|
andrewm@318
|
435 if(posix_memalign((void **)&context->audioOut, 16, 2 * context->audioFrames * sizeof(float))) {
|
andrewm@318
|
436 printf("Error allocating audio output buffer\n");
|
andrewm@318
|
437 return 1;
|
andrewm@318
|
438 }
|
andrewm@318
|
439 #else
|
andrewm@81
|
440 context->audioIn = (float *)malloc(2 * context->audioFrames * sizeof(float));
|
andrewm@81
|
441 context->audioOut = (float *)malloc(2 * context->audioFrames * sizeof(float));
|
andrewm@81
|
442 if(context->audioIn == 0 || context->audioOut == 0) {
|
andrewm@81
|
443 rt_printf("Error: couldn't allocate audio buffers\n");
|
andrewm@81
|
444 return 1;
|
andrewm@81
|
445 }
|
andrewm@318
|
446 #endif
|
andrewm@318
|
447
|
andrewm@81
|
448 // Allocate analog buffers
|
andrewm@81
|
449 if(analog_enabled) {
|
andrewm@318
|
450 #ifdef USE_NEON_FORMAT_CONVERSION
|
andrewm@318
|
451 if(posix_memalign((void **)&context->analogIn, 16,
|
andrewm@318
|
452 context->analogChannels * context->analogFrames * sizeof(float))) {
|
andrewm@318
|
453 printf("Error allocating analog input buffer\n");
|
andrewm@318
|
454 return 1;
|
andrewm@318
|
455 }
|
andrewm@318
|
456 if(posix_memalign((void **)&context->analogOut, 16,
|
andrewm@318
|
457 context->analogChannels * context->analogFrames * sizeof(float))) {
|
andrewm@318
|
458 printf("Error allocating analog output buffer\n");
|
andrewm@318
|
459 return 1;
|
andrewm@318
|
460 }
|
andrewm@318
|
461 last_analog_out_frame = (float *)malloc(context->analogChannels * sizeof(float));
|
andrewm@318
|
462
|
andrewm@318
|
463 if(last_analog_out_frame == 0) {
|
andrewm@318
|
464 rt_printf("Error: couldn't allocate analog persistence buffer\n");
|
andrewm@318
|
465 return 1;
|
andrewm@318
|
466 }
|
andrewm@318
|
467 #else
|
giuliomoro@528
|
468 context->analogIn = (float *)malloc(context->analogInChannels * context->analogFrames * sizeof(float));
|
giuliomoro@528
|
469 context->analogOut = (float *)malloc(context->analogOutChannels * context->analogFrames * sizeof(float));
|
giuliomoro@528
|
470 last_analog_out_frame = (float *)malloc(context->analogOutChannels * sizeof(float));
|
andrewm@81
|
471
|
andrewm@81
|
472 if(context->analogIn == 0 || context->analogOut == 0 || last_analog_out_frame == 0) {
|
andrewm@81
|
473 rt_printf("Error: couldn't allocate analog buffers\n");
|
andrewm@81
|
474 return 1;
|
andrewm@81
|
475 }
|
andrewm@318
|
476 #endif
|
andrewm@318
|
477
|
giuliomoro@528
|
478 memset(last_analog_out_frame, 0, context->analogOutChannels * sizeof(float));
|
andrewm@81
|
479 }
|
andrewm@81
|
480
|
andrewm@81
|
481 // Allocate digital buffers
|
andrewm@81
|
482 digital_buffer0 = pru_buffer_digital;
|
andrewm@81
|
483 digital_buffer1 = pru_buffer_digital + MEM_DIGITAL_BUFFER1_OFFSET / sizeof(uint32_t);
|
andrewm@81
|
484 if(digital_enabled) {
|
andrewm@81
|
485 last_digital_buffer = (uint32_t *)malloc(context->digitalFrames * sizeof(uint32_t)); //temp buffer to hold previous states
|
andrewm@81
|
486 if(last_digital_buffer == 0) {
|
andrewm@81
|
487 rt_printf("Error: couldn't allocate digital buffers\n");
|
andrewm@81
|
488 return 1;
|
andrewm@81
|
489 }
|
andrewm@81
|
490
|
andrewm@81
|
491 for(unsigned int n = 0; n < context->digitalFrames; n++){
|
andrewm@81
|
492 // Initialize lastDigitalFrames to all inputs
|
andrewm@81
|
493 last_digital_buffer[n] = 0x0000ffff;
|
andrewm@81
|
494 }
|
andrewm@81
|
495 }
|
andrewm@81
|
496
|
andrewm@81
|
497 context->digital = digital_buffer0;
|
andrewm@81
|
498
|
andrewm@0
|
499 return 0;
|
andrewm@0
|
500 }
|
andrewm@0
|
501
|
andrewm@0
|
502 // Run the code image in the specified file
|
giuliomoro@16
|
503 int PRU::start(char * const filename)
|
andrewm@0
|
504 {
|
andrewm@0
|
505 /* Clear any old interrupt */
|
andrewm@45
|
506 prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT);
|
andrewm@45
|
507
|
giuliomoro@16
|
508 /* Load and execute binary on PRU */
|
giuliomoro@16
|
509 if(filename[0] == '\0') { //if the string is empty, load the embedded code
|
giuliomoro@16
|
510 if(gRTAudioVerbose)
|
giuliomoro@16
|
511 rt_printf("Using embedded PRU code\n");
|
giuliomoro@16
|
512 if(prussdrv_exec_code(pru_number, PRUcode, sizeof(PRUcode))) {
|
giuliomoro@16
|
513 rt_printf("Failed to execute PRU code\n");
|
giuliomoro@16
|
514 return 1;
|
giuliomoro@16
|
515 }
|
giuliomoro@16
|
516 } else {
|
giuliomoro@16
|
517 if(gRTAudioVerbose)
|
giuliomoro@16
|
518 rt_printf("Using PRU code from %s\n",filename);
|
giuliomoro@16
|
519 if(prussdrv_exec_program(pru_number, filename)) {
|
giuliomoro@16
|
520 rt_printf("Failed to execute PRU code from %s\n", filename);
|
giuliomoro@16
|
521 return 1;
|
giuliomoro@16
|
522 }
|
giuliomoro@16
|
523 }
|
andrewm@0
|
524
|
andrewm@0
|
525 running = true;
|
andrewm@0
|
526 return 0;
|
andrewm@0
|
527 }
|
andrewm@0
|
528
|
andrewm@0
|
529 // Main loop to read and write data from/to PRU
|
andrewm@45
|
530 void PRU::loop(RT_INTR *pru_interrupt, void *userData)
|
andrewm@0
|
531 {
|
andrewm@303
|
532 #ifdef BELA_USE_XENOMAI_INTERRUPTS
|
andrewm@50
|
533 RTIME irqTimeout = PRU_SAMPLE_INTERVAL_NS * 1024; // Timeout for PRU interrupt: about 10ms, much longer than any expected period
|
andrewm@50
|
534 #else
|
andrewm@0
|
535 // Polling interval is 1/4 of the period
|
giuliomoro@528
|
536 if(context->analogInChannels != context->analogOutChannels){
|
giuliomoro@528
|
537 printf("Error: TODO: a different number of channels for inputs and outputs is not yet supported\n");
|
giuliomoro@528
|
538 return;
|
giuliomoro@528
|
539 }
|
giuliomoro@528
|
540 unsigned int analogChannels = context->analogInChannels;
|
giuliomoro@528
|
541 RTIME sleepTime = PRU_SAMPLE_INTERVAL_NS * (analogChannels / 2) * context->analogFrames / 4;
|
andrewm@50
|
542 #endif
|
andrewm@45
|
543
|
andrewm@45
|
544 uint32_t pru_audio_offset, pru_spi_offset;
|
andrewm@0
|
545
|
andrewm@81
|
546 // Before starting, look at the last state of the analog and digital outputs which might
|
andrewm@81
|
547 // have been changed by the user during the setup() function. This lets us start with pin
|
andrewm@81
|
548 // directions and output values at something other than defaults.
|
andrewm@81
|
549
|
andrewm@81
|
550 if(analog_enabled) {
|
andrewm@303
|
551 if(context->flags & BELA_FLAG_ANALOG_OUTPUTS_PERSIST) {
|
andrewm@81
|
552 // Remember the content of the last_analog_out_frame
|
giuliomoro@528
|
553 for(unsigned int ch = 0; ch < context->analogOutChannels; ch++){
|
giuliomoro@528
|
554 last_analog_out_frame[ch] = context->analogOut[context->analogOutChannels * (context->analogFrames - 1) + ch];
|
andrewm@81
|
555 }
|
andrewm@81
|
556 }
|
andrewm@0
|
557 }
|
andrewm@45
|
558
|
andrewm@45
|
559 if(digital_enabled) {
|
andrewm@45
|
560 for(unsigned int n = 0; n < context->digitalFrames; n++){
|
andrewm@81
|
561 last_digital_buffer[n] = context->digital[n];
|
andrewm@45
|
562 }
|
giuliomoro@38
|
563 }
|
andrewm@45
|
564
|
andrewm@45
|
565 // TESTING
|
andrewm@50
|
566 // uint32_t testCount = 0;
|
andrewm@45
|
567 // RTIME startTime = rt_timer_read();
|
andrewm@45
|
568
|
andrewm@303
|
569 #ifdef BELA_USE_XENOMAI_INTERRUPTS
|
andrewm@56
|
570 int result;
|
andrewm@56
|
571 #else
|
andrewm@50
|
572 // Which buffer the PRU was last processing
|
andrewm@50
|
573 uint32_t lastPRUBuffer = 0;
|
andrewm@50
|
574 #endif
|
andrewm@50
|
575
|
andrewm@0
|
576 while(!gShouldStop) {
|
andrewm@303
|
577 #ifdef BELA_USE_XENOMAI_INTERRUPTS
|
andrewm@45
|
578 // Wait for PRU to move to change buffers;
|
andrewm@45
|
579 // PRU will send an interrupts which we wait for
|
andrewm@45
|
580 rt_intr_enable(pru_interrupt);
|
andrewm@45
|
581 while(!gShouldStop) {
|
andrewm@45
|
582 result = rt_intr_wait(pru_interrupt, irqTimeout);
|
andrewm@45
|
583 if(result >= 0)
|
andrewm@45
|
584 break;
|
andrewm@45
|
585 else if(result == -ETIMEDOUT)
|
andrewm@45
|
586 rt_printf("Warning: PRU timeout!\n");
|
andrewm@45
|
587 else {
|
andrewm@45
|
588 rt_printf("Error: wait for interrupt failed (%d)\n", result);
|
andrewm@45
|
589 gShouldStop = 1;
|
andrewm@45
|
590 }
|
andrewm@0
|
591 }
|
andrewm@45
|
592
|
andrewm@45
|
593 // Clear pending PRU interrupt
|
andrewm@45
|
594 prussdrv_pru_clear_event(PRU_EVTOUT_1, PRU1_ARM_INTERRUPT);
|
andrewm@50
|
595 #else
|
andrewm@50
|
596 // Poll
|
andrewm@50
|
597 while(pru_buffer_comm[PRU_CURRENT_BUFFER] == lastPRUBuffer && !gShouldStop) {
|
andrewm@50
|
598 rt_task_sleep(sleepTime);
|
andrewm@50
|
599 }
|
andrewm@50
|
600
|
andrewm@50
|
601 lastPRUBuffer = pru_buffer_comm[PRU_CURRENT_BUFFER];
|
andrewm@50
|
602 #endif
|
andrewm@45
|
603
|
andrewm@0
|
604 if(gShouldStop)
|
andrewm@0
|
605 break;
|
andrewm@0
|
606
|
andrewm@45
|
607 // Check which buffer we're on-- will have been set right
|
andrewm@45
|
608 // before the interrupt was asserted
|
andrewm@45
|
609 if(pru_buffer_comm[PRU_CURRENT_BUFFER] == 1) {
|
andrewm@45
|
610 // PRU is on buffer 1. We read and write to buffer 0
|
andrewm@45
|
611 pru_audio_offset = 0;
|
andrewm@45
|
612 pru_spi_offset = 0;
|
andrewm@45
|
613 if(digital_enabled)
|
andrewm@81
|
614 context->digital = digital_buffer0;
|
andrewm@45
|
615 }
|
andrewm@45
|
616 else {
|
andrewm@45
|
617 // PRU is on buffer 0. We read and write to buffer 1
|
giuliomoro@528
|
618 if(context->audioInChannels != context->audioOutChannels){
|
giuliomoro@528
|
619 printf("Error: TODO: a different number of channels for inputs and outputs is not yet supported\n");
|
giuliomoro@528
|
620 return;
|
giuliomoro@528
|
621 }
|
giuliomoro@528
|
622 unsigned int audioChannels = context->audioInChannels;
|
giuliomoro@528
|
623 pru_audio_offset = context->audioFrames * audioChannels;
|
giuliomoro@528
|
624 if(context->analogInChannels != context->analogOutChannels){
|
giuliomoro@528
|
625 printf("Error: TODO: a different number of channels for inputs and outputs is not yet supported\n");
|
giuliomoro@528
|
626 return;
|
giuliomoro@528
|
627 }
|
giuliomoro@528
|
628 unsigned int analogChannels = context->analogInChannels;
|
giuliomoro@528
|
629 pru_spi_offset = context->analogFrames * analogChannels;
|
andrewm@45
|
630 if(digital_enabled)
|
andrewm@81
|
631 context->digital = digital_buffer1;
|
andrewm@45
|
632 }
|
andrewm@45
|
633
|
andrewm@45
|
634 // FIXME: some sort of margin is needed here to prevent the audio
|
andrewm@45
|
635 // code from completely eating the Linux system
|
andrewm@50
|
636 // testCount++;
|
andrewm@45
|
637 //rt_task_sleep(sleepTime*4);
|
andrewm@45
|
638 //rt_task_sleep(sleepTime/4);
|
andrewm@45
|
639
|
andrewm@0
|
640 if(xenomai_gpio != 0) {
|
andrewm@0
|
641 // Set the test pin high
|
andrewm@0
|
642 xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN_MASK;
|
andrewm@0
|
643 }
|
andrewm@0
|
644
|
andrewm@45
|
645 // Convert short (16-bit) samples to float
|
andrewm@318
|
646 #ifdef USE_NEON_FORMAT_CONVERSION
|
andrewm@318
|
647 int16_to_float_audio(2 * context->audioFrames, &pru_buffer_audio_adc[pru_audio_offset], context->audioIn);
|
andrewm@318
|
648 #else
|
andrewm@318
|
649 for(unsigned int n = 0; n < 2 * context->audioFrames; n++) {
|
giuliomoro@231
|
650 context->audioIn[n] = (float)pru_buffer_audio_adc[n + pru_audio_offset] / 32768.0f;
|
andrewm@318
|
651 }
|
andrewm@318
|
652 #endif
|
andrewm@318
|
653
|
andrewm@45
|
654 if(analog_enabled) {
|
andrewm@303
|
655 if(mux_channels != 0) {
|
andrewm@303
|
656 // If multiplexer is enabled, find out which channels we have by pulling out
|
andrewm@303
|
657 // the place that it ended.
|
andrewm@303
|
658 // int lastMuxChannel = pru_buffer_comm[PRU_MUX_END_CHANNEL];
|
andrewm@303
|
659
|
andrewm@303
|
660 // TODO
|
andrewm@303
|
661 }
|
andrewm@303
|
662
|
andrewm@318
|
663 #ifdef USE_NEON_FORMAT_CONVERSION
|
andrewm@318
|
664 int16_to_float_analog(context->analogChannels * context->analogFrames,
|
andrewm@318
|
665 &pru_buffer_spi_adc[pru_spi_offset], context->analogIn);
|
andrewm@318
|
666 #else
|
giuliomoro@528
|
667 for(unsigned int n = 0; n < context->analogInChannels * context->analogFrames; n++) {
|
giuliomoro@231
|
668 context->analogIn[n] = (float)pru_buffer_spi_adc[n + pru_spi_offset] / 65536.0f;
|
andrewm@318
|
669 }
|
andrewm@318
|
670 #endif
|
andrewm@45
|
671
|
andrewm@303
|
672 if(context->flags & BELA_FLAG_ANALOG_OUTPUTS_PERSIST) {
|
andrewm@45
|
673 // Initialize the output buffer with the values that were in the last frame of the previous output
|
giuliomoro@528
|
674 for(unsigned int ch = 0; ch < context->analogOutChannels; ch++){
|
andrewm@45
|
675 for(unsigned int n = 0; n < context->analogFrames; n++){
|
giuliomoro@528
|
676 context->analogOut[n * context->analogOutChannels + ch] = last_analog_out_frame[ch];
|
andrewm@45
|
677 }
|
giuliomoro@23
|
678 }
|
giuliomoro@23
|
679 }
|
andrewm@45
|
680 else {
|
andrewm@45
|
681 // Outputs are 0 unless set otherwise
|
giuliomoro@528
|
682 memset(context->analogOut, 0, context->analogOutChannels * context->analogFrames * sizeof(float));
|
giuliomoro@23
|
683 }
|
andrewm@45
|
684 }
|
andrewm@45
|
685
|
andrewm@45
|
686 if(digital_enabled){
|
andrewm@45
|
687 // Use past digital values to initialize the array properly.
|
andrewm@45
|
688 // For each frame:
|
andrewm@45
|
689 // - pins previously set as outputs will keep the output value they had in the last frame of the previous buffer,
|
andrewm@45
|
690 // - pins previously set as inputs will carry the newly read input value
|
andrewm@45
|
691
|
andrewm@45
|
692 for(unsigned int n = 0; n < context->digitalFrames; n++){
|
andrewm@81
|
693 uint16_t inputs = last_digital_buffer[n] & 0xffff; // half-word, has 1 for inputs and 0 for outputs
|
andrewm@45
|
694
|
andrewm@45
|
695 uint16_t outputs = ~inputs; // half-word has 1 for outputs and 0 for inputs;
|
andrewm@81
|
696 context->digital[n] = (last_digital_buffer[context->digitalFrames - 1] & (outputs << 16)) | // keep output values set in the last frame of the previous buffer
|
andrewm@45
|
697 (context->digital[n] & (inputs << 16)) | // inputs from current context->digital[n];
|
andrewm@81
|
698 (last_digital_buffer[n] & (inputs)); // keep pin configuration from previous context->digital[n]
|
andrewm@45
|
699 // context->digital[n]=digitalBufferTemp[n]; //ignores inputs
|
andrewm@45
|
700 }
|
andrewm@45
|
701 }
|
andrewm@45
|
702
|
andrewm@45
|
703 // Call user render function
|
andrewm@45
|
704 // ***********************
|
andrewm@307
|
705 render((BelaContext *)context, userData);
|
andrewm@45
|
706 // ***********************
|
andrewm@45
|
707
|
andrewm@45
|
708 if(analog_enabled) {
|
andrewm@303
|
709 if(context->flags & BELA_FLAG_ANALOG_OUTPUTS_PERSIST) {
|
andrewm@81
|
710 // Remember the content of the last_analog_out_frame
|
giuliomoro@528
|
711 for(unsigned int ch = 0; ch < context->analogOutChannels; ch++){
|
giuliomoro@528
|
712 last_analog_out_frame[ch] = context->analogOut[context->analogOutChannels * (context->analogFrames - 1) + ch];
|
andrewm@45
|
713 }
|
andrewm@45
|
714 }
|
andrewm@45
|
715
|
andrewm@45
|
716 // Convert float back to short for SPI output
|
andrewm@318
|
717 #ifdef USE_NEON_FORMAT_CONVERSION
|
andrewm@318
|
718 float_to_int16_analog(context->analogChannels * context->analogFrames,
|
andrewm@318
|
719 context->analogOut, (uint16_t*)&pru_buffer_spi_dac[pru_spi_offset]);
|
andrewm@318
|
720 #else
|
giuliomoro@528
|
721 for(unsigned int n = 0; n < context->analogOutChannels * context->analogFrames; n++) {
|
giuliomoro@231
|
722 int out = context->analogOut[n] * 65536.0f;
|
giuliomoro@16
|
723 if(out < 0) out = 0;
|
giuliomoro@16
|
724 else if(out > 65535) out = 65535;
|
andrewm@45
|
725 pru_buffer_spi_dac[n + pru_spi_offset] = (uint16_t)out;
|
giuliomoro@16
|
726 }
|
andrewm@318
|
727 #endif
|
giuliomoro@16
|
728 }
|
andrewm@45
|
729
|
andrewm@45
|
730 if(digital_enabled) { // keep track of past digital values
|
andrewm@45
|
731 for(unsigned int n = 0; n < context->digitalFrames; n++){
|
andrewm@81
|
732 last_digital_buffer[n] = context->digital[n];
|
andrewm@45
|
733 }
|
andrewm@45
|
734 }
|
andrewm@45
|
735
|
andrewm@45
|
736 // Convert float back to short for audio
|
andrewm@318
|
737 #ifdef USE_NEON_FORMAT_CONVERSION
|
andrewm@318
|
738 float_to_int16_audio(2 * context->audioFrames, context->audioOut, &pru_buffer_audio_dac[pru_audio_offset]);
|
andrewm@318
|
739 #else
|
giuliomoro@528
|
740 for(unsigned int n = 0; n < context->audioOutChannels * context->audioFrames; n++) {
|
giuliomoro@231
|
741 int out = context->audioOut[n] * 32768.0f;
|
andrewm@0
|
742 if(out < -32768) out = -32768;
|
andrewm@0
|
743 else if(out > 32767) out = 32767;
|
andrewm@45
|
744 pru_buffer_audio_dac[n + pru_audio_offset] = (int16_t)out;
|
andrewm@0
|
745 }
|
andrewm@318
|
746 #endif
|
andrewm@0
|
747
|
andrewm@52
|
748 // Increment total number of samples that have elapsed
|
andrewm@311
|
749 context->audioFramesElapsed += context->audioFrames;
|
andrewm@52
|
750
|
andrewm@0
|
751 if(xenomai_gpio != 0) {
|
andrewm@0
|
752 // Set the test pin high
|
andrewm@0
|
753 xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN_MASK;
|
andrewm@0
|
754 }
|
l@258
|
755
|
giuliomoro@301
|
756 Bela_autoScheduleAuxiliaryTasks();
|
andrewm@0
|
757
|
andrewm@45
|
758 }
|
andrewm@0
|
759
|
andrewm@303
|
760 #ifdef BELA_USE_XENOMAI_INTERRUPTS
|
andrewm@45
|
761 // Turn off the interrupt for the PRU if it isn't already off
|
andrewm@45
|
762 rt_intr_disable(pru_interrupt);
|
andrewm@50
|
763 #endif
|
andrewm@0
|
764
|
andrewm@0
|
765 // Tell PRU to stop
|
andrewm@0
|
766 pru_buffer_comm[PRU_SHOULD_STOP] = 1;
|
andrewm@0
|
767
|
andrewm@45
|
768 // Wait two buffer lengths for the PRU to finish
|
andrewm@45
|
769 rt_task_sleep(PRU_SAMPLE_INTERVAL_NS * context->analogFrames * 4 * 2);
|
andrewm@45
|
770
|
andrewm@45
|
771 // Clean up after ourselves
|
andrewm@45
|
772 free(context->audioIn);
|
andrewm@45
|
773 free(context->audioOut);
|
andrewm@45
|
774
|
andrewm@45
|
775 if(analog_enabled) {
|
andrewm@45
|
776 free(context->analogIn);
|
andrewm@45
|
777 free(context->analogOut);
|
andrewm@81
|
778 free(last_analog_out_frame);
|
andrewm@45
|
779 }
|
andrewm@45
|
780
|
andrewm@45
|
781 if(digital_enabled) {
|
andrewm@81
|
782 free(last_digital_buffer);
|
andrewm@45
|
783 }
|
andrewm@45
|
784
|
andrewm@45
|
785 context->audioIn = context->audioOut = 0;
|
andrewm@45
|
786 context->analogIn = context->analogOut = 0;
|
andrewm@45
|
787 context->digital = 0;
|
andrewm@0
|
788 }
|
andrewm@0
|
789
|
andrewm@0
|
790 // Wait for an interrupt from the PRU indicate it is finished
|
andrewm@0
|
791 void PRU::waitForFinish()
|
andrewm@0
|
792 {
|
andrewm@0
|
793 if(!running)
|
andrewm@0
|
794 return;
|
andrewm@45
|
795 prussdrv_pru_wait_event (PRU_EVTOUT_0);
|
andrewm@45
|
796 prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT);
|
andrewm@0
|
797 }
|
andrewm@0
|
798
|
andrewm@0
|
799 // Turn off the PRU when done
|
andrewm@0
|
800 void PRU::disable()
|
andrewm@0
|
801 {
|
andrewm@0
|
802 /* Disable PRU and close memory mapping*/
|
andrewm@0
|
803 prussdrv_pru_disable(pru_number);
|
andrewm@0
|
804 prussdrv_exit();
|
andrewm@0
|
805 running = false;
|
andrewm@0
|
806 }
|
andrewm@0
|
807
|
andrewm@0
|
808 // Debugging
|
andrewm@0
|
809 void PRU::setGPIOTestPin()
|
andrewm@0
|
810 {
|
andrewm@0
|
811 if(!xenomai_gpio)
|
andrewm@0
|
812 return;
|
andrewm@0
|
813 xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN2_MASK;
|
andrewm@0
|
814 }
|
andrewm@0
|
815
|
andrewm@0
|
816 void PRU::clearGPIOTestPin()
|
andrewm@0
|
817 {
|
andrewm@0
|
818 if(!xenomai_gpio)
|
andrewm@0
|
819 return;
|
andrewm@0
|
820 xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN2_MASK;
|
andrewm@0
|
821 }
|