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"
|
andrewm@0
|
21 #include "../include/render.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@0
|
40 #define PRU_MEM_MCASP_OFFSET 0x2000 // Offset within PRU-SHARED RAM
|
andrewm@0
|
41 #define PRU_MEM_MCASP_LENGTH 0x2000 // Length of McASP memory, in bytes
|
andrewm@0
|
42 #define PRU_MEM_DAC_OFFSET 0x0 // Offset within PRU0 RAM
|
andrewm@0
|
43 #define PRU_MEM_DAC_LENGTH 0x2000 // Length of ADC+DAC memory, in bytes
|
andrewm@0
|
44 #define PRU_MEM_COMM_OFFSET 0x0 // Offset within PRU-SHARED RAM
|
giuliomoro@19
|
45 #define PRU_MEM_DIGITAL_OFFSET 0x1000 //Offset within PRU-SHARED RAM
|
giuliomoro@19
|
46 #define MEM_DIGITAL_BUFFER1_OFFSET 0x400 //Start pointer to DIGITAL_BUFFER1, which is 256 words.
|
giuliomoro@16
|
47 // 256 is the maximum number of frames allowed
|
andrewm@0
|
48 #define PRU_SHOULD_STOP 0
|
andrewm@0
|
49 #define PRU_CURRENT_BUFFER 1
|
andrewm@0
|
50 #define PRU_BUFFER_FRAMES 2
|
andrewm@0
|
51 #define PRU_SHOULD_SYNC 3
|
andrewm@0
|
52 #define PRU_SYNC_ADDRESS 4
|
andrewm@0
|
53 #define PRU_SYNC_PIN_MASK 5
|
andrewm@0
|
54 #define PRU_LED_ADDRESS 6
|
andrewm@0
|
55 #define PRU_LED_PIN_MASK 7
|
andrewm@0
|
56 #define PRU_FRAME_COUNT 8
|
andrewm@0
|
57 #define PRU_USE_SPI 9
|
andrewm@12
|
58 #define PRU_SPI_NUM_CHANNELS 10
|
giuliomoro@38
|
59 #define PRU_USE_DIGITAL 11
|
giuliomoro@16
|
60
|
giuliomoro@19
|
61 short int digitalPins[NUM_DIGITALS]={
|
giuliomoro@16
|
62 GPIO_NO_BIT_0,
|
giuliomoro@16
|
63 GPIO_NO_BIT_1,
|
giuliomoro@16
|
64 GPIO_NO_BIT_2,
|
giuliomoro@16
|
65 GPIO_NO_BIT_3,
|
giuliomoro@16
|
66 GPIO_NO_BIT_4,
|
giuliomoro@16
|
67 GPIO_NO_BIT_5,
|
giuliomoro@16
|
68 GPIO_NO_BIT_6,
|
giuliomoro@16
|
69 GPIO_NO_BIT_7,
|
giuliomoro@16
|
70 GPIO_NO_BIT_8,
|
giuliomoro@16
|
71 GPIO_NO_BIT_9,
|
giuliomoro@16
|
72 GPIO_NO_BIT_10,
|
giuliomoro@16
|
73 GPIO_NO_BIT_11,
|
giuliomoro@16
|
74 GPIO_NO_BIT_12,
|
giuliomoro@16
|
75 GPIO_NO_BIT_13,
|
giuliomoro@16
|
76 GPIO_NO_BIT_14,
|
giuliomoro@16
|
77 GPIO_NO_BIT_15,
|
giuliomoro@16
|
78 };
|
andrewm@0
|
79
|
andrewm@12
|
80 #define PRU_SAMPLE_INTERVAL_NS 11338 // 88200Hz per SPI sample = 11.338us
|
andrewm@0
|
81
|
andrewm@0
|
82 #define GPIO0_ADDRESS 0x44E07000
|
andrewm@0
|
83 #define GPIO1_ADDRESS 0x4804C000
|
andrewm@0
|
84 #define GPIO_SIZE 0x198
|
andrewm@0
|
85 #define GPIO_CLEARDATAOUT (0x190 / 4)
|
andrewm@0
|
86 #define GPIO_SETDATAOUT (0x194 / 4)
|
andrewm@0
|
87
|
andrewm@0
|
88 #define TEST_PIN_GPIO_BASE GPIO0_ADDRESS // Use GPIO0(31) for debugging
|
andrewm@0
|
89 #define TEST_PIN_MASK (1 << 31)
|
andrewm@0
|
90 #define TEST_PIN2_MASK (1 << 26)
|
andrewm@0
|
91
|
andrewm@0
|
92 #define USERLED3_GPIO_BASE GPIO1_ADDRESS // GPIO1(24) is user LED 3
|
andrewm@0
|
93 #define USERLED3_PIN_MASK (1 << 24)
|
andrewm@0
|
94
|
andrewm@0
|
95 const unsigned int PRU::kPruGPIODACSyncPin = 5; // GPIO0(5); P9-17
|
andrewm@0
|
96 const unsigned int PRU::kPruGPIOADCSyncPin = 48; // GPIO1(16); P9-15
|
andrewm@0
|
97
|
andrewm@0
|
98 const unsigned int PRU::kPruGPIOTestPin = 60; // GPIO1(28); P9-12
|
andrewm@0
|
99 const unsigned int PRU::kPruGPIOTestPin2 = 31; // GPIO0(31); P9-13
|
andrewm@0
|
100 const unsigned int PRU::kPruGPIOTestPin3 = 26; // GPIO0(26); P8-14
|
andrewm@0
|
101
|
andrewm@0
|
102 extern int gShouldStop;
|
andrewm@0
|
103 extern int gRTAudioVerbose;
|
andrewm@0
|
104
|
andrewm@0
|
105 // Constructor: specify a PRU number (0 or 1)
|
andrewm@0
|
106 PRU::PRU()
|
andrewm@0
|
107 : pru_number(0), running(false), spi_enabled(false), gpio_enabled(false), led_enabled(false),
|
andrewm@12
|
108 gpio_test_pin_enabled(false), spi_num_channels(0), xenomai_gpio_fd(-1), xenomai_gpio(0)
|
andrewm@0
|
109 {
|
andrewm@0
|
110
|
andrewm@0
|
111 }
|
andrewm@0
|
112
|
andrewm@0
|
113 // Destructor
|
andrewm@0
|
114 PRU::~PRU()
|
andrewm@0
|
115 {
|
andrewm@0
|
116 if(running)
|
andrewm@0
|
117 disable();
|
andrewm@0
|
118 if(gpio_enabled)
|
andrewm@0
|
119 cleanupGPIO();
|
andrewm@0
|
120 if(xenomai_gpio_fd >= 0)
|
andrewm@0
|
121 close(xenomai_gpio_fd);
|
andrewm@0
|
122 }
|
andrewm@0
|
123
|
andrewm@0
|
124 // Prepare the GPIO pins needed for the PRU
|
andrewm@0
|
125 // If include_test_pin is set, the GPIO output
|
andrewm@0
|
126 // is also prepared for an output which can be
|
andrewm@0
|
127 // viewed on a scope. If include_led is set,
|
andrewm@0
|
128 // user LED 3 on the BBB is taken over by the PRU
|
andrewm@0
|
129 // to indicate activity
|
giuliomoro@19
|
130 int PRU::prepareGPIO(int use_spi, int use_digital, int include_test_pin, int include_led)
|
andrewm@0
|
131 {
|
andrewm@0
|
132 if(use_spi) {
|
andrewm@0
|
133 // Prepare DAC CS/ pin: output, high to begin
|
andrewm@0
|
134 if(gpio_export(kPruGPIODACSyncPin)) {
|
andrewm@0
|
135 if(gRTAudioVerbose)
|
andrewm@0
|
136 cout << "Warning: couldn't export DAC sync pin\n";
|
andrewm@0
|
137 }
|
andrewm@0
|
138 if(gpio_set_dir(kPruGPIODACSyncPin, OUTPUT_PIN)) {
|
andrewm@0
|
139 if(gRTAudioVerbose)
|
andrewm@0
|
140 cout << "Couldn't set direction on DAC sync pin\n";
|
andrewm@0
|
141 return -1;
|
andrewm@0
|
142 }
|
andrewm@0
|
143 if(gpio_set_value(kPruGPIODACSyncPin, HIGH)) {
|
andrewm@0
|
144 if(gRTAudioVerbose)
|
andrewm@0
|
145 cout << "Couldn't set value on DAC sync pin\n";
|
andrewm@0
|
146 return -1;
|
andrewm@0
|
147 }
|
andrewm@0
|
148
|
andrewm@0
|
149 // Prepare ADC CS/ pin: output, high to begin
|
andrewm@0
|
150 if(gpio_export(kPruGPIOADCSyncPin)) {
|
andrewm@0
|
151 if(gRTAudioVerbose)
|
andrewm@0
|
152 cout << "Warning: couldn't export ADC sync pin\n";
|
andrewm@0
|
153 }
|
andrewm@0
|
154 if(gpio_set_dir(kPruGPIOADCSyncPin, OUTPUT_PIN)) {
|
andrewm@0
|
155 if(gRTAudioVerbose)
|
andrewm@0
|
156 cout << "Couldn't set direction on ADC sync pin\n";
|
andrewm@0
|
157 return -1;
|
andrewm@0
|
158 }
|
andrewm@0
|
159 if(gpio_set_value(kPruGPIOADCSyncPin, HIGH)) {
|
andrewm@0
|
160 if(gRTAudioVerbose)
|
andrewm@0
|
161 cout << "Couldn't set value on ADC sync pin\n";
|
andrewm@0
|
162 return -1;
|
andrewm@0
|
163 }
|
andrewm@0
|
164
|
andrewm@0
|
165 spi_enabled = true;
|
andrewm@0
|
166 }
|
andrewm@0
|
167
|
giuliomoro@19
|
168 if(use_digital){
|
giuliomoro@19
|
169 printf("gNumDigitalChannels: %d;\n",gNumDigitalChannels);
|
giuliomoro@19
|
170 for(int i=0; i<gNumDigitalChannels; i++){
|
giuliomoro@19
|
171 if(gpio_export(digitalPins[i])) {
|
giuliomoro@16
|
172 if(gRTAudioVerbose)
|
giuliomoro@38
|
173 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
|
174 }
|
giuliomoro@38
|
175 if(gpio_set_dir(digitalPins[i], INPUT_PIN)) {
|
giuliomoro@16
|
176 if(gRTAudioVerbose)
|
giuliomoro@38
|
177 cerr << "Error: Couldn't set direction on digital GPIO pin " << digitalPins[i] << "\n";
|
giuliomoro@16
|
178 return -1;
|
giuliomoro@16
|
179 }
|
giuliomoro@16
|
180 }
|
giuliomoro@19
|
181 digital_enabled=true;
|
giuliomoro@16
|
182 }
|
giuliomoro@16
|
183
|
andrewm@0
|
184 if(include_test_pin) {
|
andrewm@0
|
185 // Prepare GPIO test output (for debugging), low to begin
|
andrewm@0
|
186 if(gpio_export(kPruGPIOTestPin)) {
|
andrewm@0
|
187 if(gRTAudioVerbose)
|
andrewm@0
|
188 cout << "Warning: couldn't export GPIO test pin\n";
|
andrewm@0
|
189 }
|
andrewm@0
|
190 if(gpio_set_dir(kPruGPIOTestPin, OUTPUT_PIN)) {
|
andrewm@0
|
191 if(gRTAudioVerbose)
|
andrewm@0
|
192 cout << "Couldn't set direction on GPIO test pin\n";
|
andrewm@0
|
193 return -1;
|
andrewm@0
|
194 }
|
andrewm@0
|
195 if(gpio_set_value(kPruGPIOTestPin, LOW)) {
|
andrewm@0
|
196 if(gRTAudioVerbose)
|
andrewm@0
|
197 cout << "Couldn't set value on GPIO test pin\n";
|
andrewm@0
|
198 return -1;
|
andrewm@0
|
199 }
|
andrewm@0
|
200
|
andrewm@0
|
201 if(gpio_export(kPruGPIOTestPin2)) {
|
andrewm@0
|
202 if(gRTAudioVerbose)
|
andrewm@0
|
203 cout << "Warning: couldn't export GPIO test pin 2\n";
|
andrewm@0
|
204 }
|
andrewm@0
|
205 if(gpio_set_dir(kPruGPIOTestPin2, OUTPUT_PIN)) {
|
andrewm@0
|
206 if(gRTAudioVerbose)
|
andrewm@0
|
207 cout << "Couldn't set direction on GPIO test pin 2\n";
|
andrewm@0
|
208 return -1;
|
andrewm@0
|
209 }
|
andrewm@0
|
210 if(gpio_set_value(kPruGPIOTestPin2, LOW)) {
|
andrewm@0
|
211 if(gRTAudioVerbose)
|
andrewm@0
|
212 cout << "Couldn't set value on GPIO test pin 2\n";
|
andrewm@0
|
213 return -1;
|
andrewm@0
|
214 }
|
andrewm@0
|
215
|
andrewm@0
|
216 if(gpio_export(kPruGPIOTestPin3)) {
|
andrewm@0
|
217 if(gRTAudioVerbose)
|
andrewm@0
|
218 cout << "Warning: couldn't export GPIO test pin 3\n";
|
andrewm@0
|
219 }
|
andrewm@0
|
220 if(gpio_set_dir(kPruGPIOTestPin3, OUTPUT_PIN)) {
|
andrewm@0
|
221 if(gRTAudioVerbose)
|
andrewm@0
|
222 cout << "Couldn't set direction on GPIO test pin 3\n";
|
andrewm@0
|
223 return -1;
|
andrewm@0
|
224 }
|
andrewm@0
|
225 if(gpio_set_value(kPruGPIOTestPin3, LOW)) {
|
andrewm@0
|
226 if(gRTAudioVerbose)
|
andrewm@0
|
227 cout << "Couldn't set value on GPIO test pin 3\n";
|
andrewm@0
|
228 return -1;
|
andrewm@0
|
229 }
|
andrewm@0
|
230 gpio_test_pin_enabled = true;
|
andrewm@0
|
231 }
|
andrewm@0
|
232
|
andrewm@0
|
233 if(include_led) {
|
andrewm@0
|
234 // Turn off system function for LED3 so it can be reused by PRU
|
andrewm@0
|
235 led_set_trigger(3, "none");
|
andrewm@0
|
236 led_enabled = true;
|
andrewm@0
|
237 }
|
andrewm@0
|
238
|
andrewm@0
|
239 gpio_enabled = true;
|
andrewm@0
|
240
|
andrewm@0
|
241 return 0;
|
andrewm@0
|
242 }
|
andrewm@0
|
243
|
andrewm@0
|
244 // Clean up the GPIO at the end
|
andrewm@0
|
245 void PRU::cleanupGPIO()
|
andrewm@0
|
246 {
|
andrewm@0
|
247 if(!gpio_enabled)
|
andrewm@0
|
248 return;
|
andrewm@0
|
249 if(spi_enabled) {
|
andrewm@0
|
250 gpio_unexport(kPruGPIODACSyncPin);
|
andrewm@0
|
251 gpio_unexport(kPruGPIOADCSyncPin);
|
andrewm@0
|
252 }
|
giuliomoro@19
|
253 if(digital_enabled){
|
giuliomoro@19
|
254 for(int i=0; i<gNumDigitalChannels; i++){
|
giuliomoro@19
|
255 gpio_unexport(digitalPins[i]);
|
giuliomoro@16
|
256 }
|
giuliomoro@16
|
257 }
|
andrewm@0
|
258 if(gpio_test_pin_enabled) {
|
andrewm@0
|
259 gpio_unexport(kPruGPIOTestPin);
|
andrewm@0
|
260 gpio_unexport(kPruGPIOTestPin2);
|
andrewm@0
|
261 gpio_unexport(kPruGPIOTestPin3);
|
andrewm@0
|
262 }
|
andrewm@0
|
263 if(led_enabled) {
|
andrewm@0
|
264 // Set LED back to default eMMC status
|
andrewm@0
|
265 // TODO: make it go back to its actual value before this program,
|
andrewm@0
|
266 // rather than the system default
|
andrewm@0
|
267 led_set_trigger(3, "mmc1");
|
andrewm@0
|
268 }
|
andrewm@0
|
269 gpio_enabled = gpio_test_pin_enabled = false;
|
andrewm@0
|
270 }
|
andrewm@0
|
271
|
andrewm@0
|
272 // Initialise and open the PRU
|
andrewm@12
|
273 int PRU::initialise(int pru_num, int frames_per_buffer, int spi_channels, bool xenomai_test_pin)
|
andrewm@0
|
274 {
|
andrewm@0
|
275 uint32_t *pruMem = 0;
|
andrewm@0
|
276
|
andrewm@0
|
277 if(!gpio_enabled) {
|
andrewm@0
|
278 rt_printf("initialise() called before GPIO enabled\n");
|
andrewm@0
|
279 return 1;
|
andrewm@0
|
280 }
|
andrewm@0
|
281
|
andrewm@0
|
282 pru_number = pru_num;
|
andrewm@0
|
283
|
andrewm@12
|
284 /* Set number of SPI ADC / DAC channels to use. This implicitly
|
andrewm@12
|
285 * also determines the sample rate relative to the audio clock
|
andrewm@12
|
286 * (half audio clock for 8 channels, full audio clock for 4,
|
andrewm@12
|
287 * double audio clock for 2)
|
andrewm@12
|
288 */
|
andrewm@12
|
289 spi_num_channels = spi_channels;
|
andrewm@12
|
290
|
andrewm@0
|
291 /* Initialize structure used by prussdrv_pruintc_intc */
|
andrewm@0
|
292 /* PRUSS_INTC_INITDATA is found in pruss_intc_mapping.h */
|
andrewm@0
|
293 tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA;
|
andrewm@0
|
294
|
andrewm@0
|
295 /* Allocate and initialize memory */
|
andrewm@0
|
296 prussdrv_init();
|
andrewm@15
|
297 if(prussdrv_open(pru_number == 0 ? PRU_EVTOUT_0 : PRU_EVTOUT_1)) {
|
andrewm@0
|
298 rt_printf("Failed to open PRU driver\n");
|
andrewm@0
|
299 return 1;
|
andrewm@0
|
300 }
|
andrewm@0
|
301
|
andrewm@0
|
302 /* Map PRU's INTC */
|
andrewm@0
|
303 prussdrv_pruintc_init(&pruss_intc_initdata);
|
andrewm@0
|
304
|
andrewm@0
|
305 spi_buffer_frames = frames_per_buffer;
|
andrewm@12
|
306 audio_buffer_frames = spi_buffer_frames * spi_num_channels / 4;
|
giuliomoro@19
|
307 digital_buffer_frames = audio_buffer_frames;
|
andrewm@0
|
308
|
andrewm@0
|
309 /* Map PRU memory to pointers */
|
andrewm@0
|
310 prussdrv_map_prumem (PRUSS0_SHARED_DATARAM, (void **)&pruMem);
|
andrewm@0
|
311 pru_buffer_comm = (uint32_t *)&pruMem[PRU_MEM_COMM_OFFSET/sizeof(uint32_t)];
|
andrewm@0
|
312 pru_buffer_audio_dac = (int16_t *)&pruMem[PRU_MEM_MCASP_OFFSET/sizeof(uint32_t)];
|
andrewm@0
|
313
|
andrewm@12
|
314 /* ADC memory starts 2(ch)*2(buffers)*bufsize samples later */
|
andrewm@12
|
315 pru_buffer_audio_adc = &pru_buffer_audio_dac[4 * audio_buffer_frames];
|
andrewm@0
|
316
|
andrewm@0
|
317 if(spi_enabled) {
|
andrewm@0
|
318 prussdrv_map_prumem (pru_number == 0 ? PRUSS0_PRU0_DATARAM : PRUSS0_PRU1_DATARAM, (void **)&pruMem);
|
andrewm@0
|
319 pru_buffer_spi_dac = (uint16_t *)&pruMem[PRU_MEM_DAC_OFFSET/sizeof(uint32_t)];
|
andrewm@0
|
320
|
andrewm@12
|
321 /* ADC memory starts after N(ch)*2(buffers)*bufsize samples */
|
andrewm@12
|
322 pru_buffer_spi_adc = &pru_buffer_spi_dac[2 * spi_num_channels * spi_buffer_frames];
|
andrewm@0
|
323 }
|
andrewm@0
|
324 else {
|
andrewm@0
|
325 pru_buffer_spi_dac = pru_buffer_spi_adc = 0;
|
andrewm@0
|
326 }
|
andrewm@0
|
327
|
giuliomoro@19
|
328 if(digital_enabled) {
|
giuliomoro@16
|
329 prussdrv_map_prumem (PRUSS0_SHARED_DATARAM, (void **)&pruMem);
|
giuliomoro@19
|
330 pru_buffer_digital = (uint32_t *)&pruMem[PRU_MEM_DIGITAL_OFFSET/sizeof(uint32_t)];
|
giuliomoro@16
|
331 }
|
giuliomoro@16
|
332 else {
|
giuliomoro@19
|
333 pru_buffer_digital = 0;
|
giuliomoro@16
|
334 }
|
andrewm@0
|
335 /* Set up flags */
|
andrewm@0
|
336 pru_buffer_comm[PRU_SHOULD_STOP] = 0;
|
andrewm@0
|
337 pru_buffer_comm[PRU_CURRENT_BUFFER] = 0;
|
andrewm@0
|
338 pru_buffer_comm[PRU_BUFFER_FRAMES] = spi_buffer_frames;
|
andrewm@0
|
339 pru_buffer_comm[PRU_SHOULD_SYNC] = 0;
|
andrewm@0
|
340 pru_buffer_comm[PRU_SYNC_ADDRESS] = 0;
|
andrewm@0
|
341 pru_buffer_comm[PRU_SYNC_PIN_MASK] = 0;
|
andrewm@0
|
342 if(led_enabled) {
|
andrewm@0
|
343 pru_buffer_comm[PRU_LED_ADDRESS] = USERLED3_GPIO_BASE;
|
andrewm@0
|
344 pru_buffer_comm[PRU_LED_PIN_MASK] = USERLED3_PIN_MASK;
|
andrewm@0
|
345 }
|
andrewm@0
|
346 else {
|
andrewm@0
|
347 pru_buffer_comm[PRU_LED_ADDRESS] = 0;
|
andrewm@0
|
348 pru_buffer_comm[PRU_LED_PIN_MASK] = 0;
|
andrewm@0
|
349 }
|
andrewm@0
|
350 if(spi_enabled) {
|
andrewm@0
|
351 pru_buffer_comm[PRU_USE_SPI] = 1;
|
andrewm@12
|
352 pru_buffer_comm[PRU_SPI_NUM_CHANNELS] = spi_num_channels;
|
andrewm@0
|
353 }
|
andrewm@0
|
354 else {
|
andrewm@0
|
355 pru_buffer_comm[PRU_USE_SPI] = 0;
|
andrewm@12
|
356 pru_buffer_comm[PRU_SPI_NUM_CHANNELS] = 0;
|
andrewm@0
|
357 }
|
giuliomoro@19
|
358 if(digital_enabled) {
|
giuliomoro@38
|
359 pru_buffer_comm[PRU_USE_DIGITAL] = 1;
|
giuliomoro@38
|
360 //TODO: add mask
|
giuliomoro@16
|
361 }
|
giuliomoro@16
|
362 else {
|
giuliomoro@38
|
363 pru_buffer_comm[PRU_USE_DIGITAL] = 0;
|
giuliomoro@38
|
364
|
giuliomoro@16
|
365 }
|
andrewm@0
|
366
|
giuliomoro@38
|
367 /* Clear ADC and DAC memory.*/
|
giuliomoro@38
|
368 //TODO: this initialisation should only address the memory effectively used by these buffers, i.e.:depend on the number of frames
|
giuliomoro@38
|
369 // (otherwise might cause issues if we move memory locations later on)
|
andrewm@0
|
370 if(spi_enabled) {
|
andrewm@0
|
371 for(int i = 0; i < PRU_MEM_DAC_LENGTH / 2; i++)
|
andrewm@0
|
372 pru_buffer_spi_dac[i] = 0;
|
giuliomoro@38
|
373 if(digital_enabled){
|
giuliomoro@38
|
374 for(int i = 0; i < PRU_MEM_DIGITAL_OFFSET*2; i++)
|
giuliomoro@38
|
375 pru_buffer_digital[i] = 0x0000ffff; // set to all inputs, to avoid unexpected spikes
|
giuliomoro@38
|
376 }
|
andrewm@0
|
377 }
|
andrewm@0
|
378 for(int i = 0; i < PRU_MEM_MCASP_LENGTH / 2; i++)
|
andrewm@0
|
379 pru_buffer_audio_dac[i] = 0;
|
giuliomoro@16
|
380 //TODO: maybe the lines below are to be deleted, as we removed the test code from pru_rtaudio.p ?
|
andrewm@0
|
381 /* If using GPIO test pin for Xenomai (for debugging), initialise the pointer now */
|
andrewm@0
|
382 if(xenomai_test_pin && xenomai_gpio_fd < 0) {
|
andrewm@0
|
383 xenomai_gpio_fd = open("/dev/mem", O_RDWR);
|
andrewm@0
|
384 if(xenomai_gpio_fd < 0)
|
andrewm@0
|
385 rt_printf("Unable to open /dev/mem for GPIO test pin\n");
|
andrewm@0
|
386 else {
|
andrewm@0
|
387 xenomai_gpio = (uint32_t *)mmap(0, GPIO_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, xenomai_gpio_fd, TEST_PIN_GPIO_BASE);
|
andrewm@0
|
388 if(xenomai_gpio == MAP_FAILED) {
|
andrewm@0
|
389 rt_printf("Unable to map GPIO address for test pin\n");
|
andrewm@0
|
390 xenomai_gpio = 0;
|
andrewm@0
|
391 close(xenomai_gpio_fd);
|
andrewm@0
|
392 xenomai_gpio_fd = -1;
|
andrewm@0
|
393 }
|
andrewm@0
|
394 }
|
andrewm@0
|
395 }
|
andrewm@0
|
396
|
andrewm@0
|
397 return 0;
|
andrewm@0
|
398 }
|
andrewm@0
|
399
|
andrewm@0
|
400 // Run the code image in the specified file
|
giuliomoro@16
|
401 int PRU::start(char * const filename)
|
andrewm@0
|
402 {
|
andrewm@0
|
403 /* Clear any old interrupt */
|
andrewm@15
|
404 if(pru_number == 0)
|
andrewm@15
|
405 prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT);
|
andrewm@15
|
406 else
|
andrewm@15
|
407 prussdrv_pru_clear_event(PRU_EVTOUT_1, PRU1_ARM_INTERRUPT);
|
giuliomoro@16
|
408 /* Load and execute binary on PRU */
|
giuliomoro@16
|
409 if(filename[0] == '\0') { //if the string is empty, load the embedded code
|
giuliomoro@16
|
410 if(gRTAudioVerbose)
|
giuliomoro@16
|
411 rt_printf("Using embedded PRU code\n");
|
giuliomoro@16
|
412 if(prussdrv_exec_code(pru_number, PRUcode, sizeof(PRUcode))) {
|
giuliomoro@16
|
413 rt_printf("Failed to execute PRU code\n");
|
giuliomoro@16
|
414 return 1;
|
giuliomoro@16
|
415 }
|
giuliomoro@16
|
416 } else {
|
giuliomoro@16
|
417 if(gRTAudioVerbose)
|
giuliomoro@16
|
418 rt_printf("Using PRU code from %s\n",filename);
|
giuliomoro@16
|
419 if(prussdrv_exec_program(pru_number, filename)) {
|
giuliomoro@16
|
420 rt_printf("Failed to execute PRU code from %s\n", filename);
|
giuliomoro@16
|
421 return 1;
|
giuliomoro@16
|
422 }
|
giuliomoro@16
|
423 }
|
andrewm@0
|
424
|
andrewm@0
|
425 running = true;
|
andrewm@0
|
426 return 0;
|
andrewm@0
|
427 }
|
giuliomoro@16
|
428 uint32_t empty[1024]={0x0};
|
andrewm@0
|
429
|
andrewm@0
|
430 // Main loop to read and write data from/to PRU
|
andrewm@0
|
431 void PRU::loop()
|
andrewm@0
|
432 {
|
andrewm@0
|
433 // Polling interval is 1/4 of the period
|
andrewm@12
|
434 RTIME sleepTime = PRU_SAMPLE_INTERVAL_NS * (spi_num_channels / 2) * spi_buffer_frames / 4;
|
andrewm@0
|
435 float *audioInBuffer, *audioOutBuffer;
|
giuliomoro@23
|
436 float *analogInBuffer, *analogOutBuffer, *lastAnalogOutFrame;
|
giuliomoro@23
|
437 uint32_t *digitalBuffer0, *digitalBuffer1, *lastDigitalBuffer;
|
andrewm@0
|
438
|
andrewm@0
|
439 audioInBuffer = (float *)malloc(2 * audio_buffer_frames * sizeof(float));
|
andrewm@0
|
440 audioOutBuffer = (float *)malloc(2 * audio_buffer_frames * sizeof(float));
|
giuliomoro@19
|
441 analogInBuffer = (float *)malloc(spi_num_channels * spi_buffer_frames * sizeof(float));
|
giuliomoro@19
|
442 analogOutBuffer = (float *)malloc(spi_num_channels * spi_buffer_frames * sizeof(float));
|
giuliomoro@23
|
443 lastAnalogOutFrame = (float *)malloc(spi_num_channels * sizeof(float));
|
giuliomoro@19
|
444 digitalBuffer0 = pru_buffer_digital;
|
giuliomoro@19
|
445 digitalBuffer1 = pru_buffer_digital+MEM_DIGITAL_BUFFER1_OFFSET/sizeof(uint32_t);
|
giuliomoro@23
|
446 digital_buffer_frames = digital_enabled ? audio_buffer_frames : 0; //TODO: find a more elegant solution for when the digital is disabled e.g.:
|
giuliomoro@19
|
447 // - embed in the digitalWrite/Read macros a check whether digital is enabled
|
giuliomoro@16
|
448 // - allocate some memory in ARM just to allow render() to run regardless.
|
giuliomoro@19
|
449 // in this case it can be digitalBuffer0 == digitalBuffer1
|
giuliomoro@19
|
450 printf("digital_buffer_frames: %d;\n",digital_buffer_frames);
|
giuliomoro@23
|
451 lastDigitalBuffer = (uint32_t *)malloc(digital_buffer_frames*sizeof(uint32_t)); //temp buffer to hold previous states
|
andrewm@0
|
452 if(audioInBuffer == 0 || audioOutBuffer == 0) {
|
giuliomoro@16
|
453 rt_printf("Error: couldn't allocate audio buffers\n");
|
andrewm@0
|
454 return;
|
andrewm@0
|
455 }
|
giuliomoro@23
|
456 if(analogInBuffer == 0 || analogOutBuffer == 0 || lastAnalogOutFrame == 0) {
|
giuliomoro@19
|
457 rt_printf("Error: couldn't allocate analog buffers\n");
|
giuliomoro@16
|
458 return;
|
giuliomoro@16
|
459 }
|
giuliomoro@23
|
460 if(lastDigitalBuffer == 0) {
|
giuliomoro@19
|
461 rt_printf("Error: couldn't allocate digital buffers\n");
|
giuliomoro@16
|
462 return;
|
giuliomoro@23
|
463 }
|
giuliomoro@38
|
464
|
giuliomoro@38
|
465 for(unsigned int n=0; n<digital_buffer_frames; n++){ //initialize lastDigitalFrames to all inputs
|
giuliomoro@38
|
466 lastDigitalBuffer[n]= 0x0000ffff;
|
giuliomoro@38
|
467 }
|
andrewm@0
|
468 while(!gShouldStop) {
|
andrewm@0
|
469 // Wait for PRU to move to buffer 1
|
andrewm@0
|
470 while(pru_buffer_comm[PRU_CURRENT_BUFFER] == 0 && !gShouldStop) {
|
andrewm@0
|
471 rt_task_sleep(sleepTime);
|
andrewm@0
|
472 }
|
andrewm@0
|
473 if(gShouldStop)
|
andrewm@0
|
474 break;
|
andrewm@0
|
475
|
andrewm@0
|
476 if(xenomai_gpio != 0) {
|
andrewm@0
|
477 // Set the test pin high
|
andrewm@0
|
478 xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN_MASK;
|
andrewm@0
|
479 }
|
andrewm@0
|
480
|
andrewm@0
|
481 // Render from/to buffer 0
|
andrewm@0
|
482
|
andrewm@0
|
483 // Convert short (16-bit) samples to float
|
andrewm@0
|
484 for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++)
|
andrewm@0
|
485 audioInBuffer[n] = (float)pru_buffer_audio_adc[n] / 32768.0;
|
giuliomoro@16
|
486 if(spi_enabled) {
|
giuliomoro@16
|
487 for(unsigned int n = 0; n < spi_num_channels * spi_buffer_frames; n++)
|
giuliomoro@19
|
488 analogInBuffer[n] = (float)pru_buffer_spi_adc[n] / 65536.0;
|
giuliomoro@23
|
489 //initialize the output buffer with the values that were in the last frame of the previous output
|
giuliomoro@23
|
490 for(int n = 0; n < spi_num_channels; n++){
|
giuliomoro@23
|
491 for(unsigned int j = 0; j < spi_buffer_frames; j++){
|
giuliomoro@23
|
492 analogOutBuffer[j*spi_buffer_frames + n] = lastAnalogOutFrame[n];
|
giuliomoro@23
|
493 }
|
giuliomoro@23
|
494 }
|
giuliomoro@23
|
495 //use past digital values to initialize the array properly.
|
giuliomoro@23
|
496 //For each frame:
|
giuliomoro@23
|
497 //- pins previously set as outputs will keep the output value they had in the last frame of the previous buffer,
|
giuliomoro@16
|
498 //- pins previously set as inputs will carry the newly read input value
|
giuliomoro@19
|
499 if(digital_enabled){
|
giuliomoro@19
|
500 for(unsigned int n = 0; n < digital_buffer_frames; n++){
|
giuliomoro@23
|
501 uint16_t inputs=lastDigitalBuffer[n]&0xffff;//half-word, has 1 for inputs and 0 for outputs
|
giuliomoro@16
|
502 // printf("inputs: 0x%x\n",inputs);
|
giuliomoro@16
|
503 uint16_t outputs=~inputs; //half-word has 1 for outputs and 0 for inputs;
|
giuliomoro@23
|
504 digitalBuffer0[n]=(lastDigitalBuffer[digital_buffer_frames-1]&(outputs<<16))| //keep output values set in the last frame of the previous buffer
|
giuliomoro@19
|
505 (digitalBuffer0[n]&(inputs<<16)) | //inputs from current digitalBuffer0[n];
|
giuliomoro@23
|
506 (lastDigitalBuffer[n]&(inputs)); //keep pin configuration from previous digitalBuffer1[n]
|
giuliomoro@19
|
507 // digitalBuffer0[n]=digitalBufferTemp[n]; //ignores inputs
|
giuliomoro@16
|
508 }
|
giuliomoro@16
|
509 }
|
giuliomoro@19
|
510 render(spi_buffer_frames, digital_buffer_frames, audio_buffer_frames, audioInBuffer, audioOutBuffer,
|
giuliomoro@19
|
511 analogInBuffer, analogOutBuffer, digitalBuffer0);
|
giuliomoro@23
|
512 //remember the content of the lastAnalogOutFrame
|
giuliomoro@23
|
513 for(int n = 0; n < spi_num_channels; n++){
|
giuliomoro@23
|
514 lastAnalogOutFrame[n] = analogOutBuffer[spi_buffer_frames*(spi_buffer_frames-1) + n];
|
giuliomoro@23
|
515 }
|
giuliomoro@16
|
516 for(unsigned int n = 0; n < spi_num_channels * spi_buffer_frames; n++) {
|
giuliomoro@19
|
517 int out = analogOutBuffer[n] * 65536.0;
|
giuliomoro@16
|
518 if(out < 0) out = 0;
|
giuliomoro@16
|
519 else if(out > 65535) out = 65535;
|
giuliomoro@16
|
520 pru_buffer_spi_dac[n] = (uint16_t)out;
|
giuliomoro@16
|
521 }
|
giuliomoro@19
|
522 if(digital_enabled){ // keep track of past digital values
|
giuliomoro@19
|
523 for(unsigned int n = 0; n < digital_buffer_frames; n++){
|
giuliomoro@23
|
524 lastDigitalBuffer[n]=digitalBuffer0[n];
|
giuliomoro@16
|
525 }
|
giuliomoro@16
|
526 }
|
giuliomoro@16
|
527 }
|
andrewm@0
|
528 else
|
giuliomoro@19
|
529 render(0, 0, audio_buffer_frames, audioInBuffer, audioOutBuffer, 0, 0, 0); // we still pass digitalBuffer, just it is unused
|
giuliomoro@16
|
530 // Convert float back to short
|
andrewm@0
|
531 for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++) {
|
andrewm@0
|
532 int out = audioOutBuffer[n] * 32768.0;
|
andrewm@0
|
533 if(out < -32768) out = -32768;
|
andrewm@0
|
534 else if(out > 32767) out = 32767;
|
andrewm@0
|
535 pru_buffer_audio_dac[n] = (int16_t)out;
|
andrewm@0
|
536 }
|
andrewm@0
|
537
|
andrewm@0
|
538 if(xenomai_gpio != 0) {
|
andrewm@0
|
539 // Set the test pin high
|
andrewm@0
|
540 xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN_MASK;
|
andrewm@0
|
541 }
|
andrewm@0
|
542
|
andrewm@0
|
543 // Wait for PRU to move to buffer 0
|
andrewm@0
|
544 while(pru_buffer_comm[PRU_CURRENT_BUFFER] != 0 && !gShouldStop) {
|
andrewm@0
|
545 rt_task_sleep(sleepTime);
|
andrewm@0
|
546 }
|
andrewm@0
|
547
|
andrewm@0
|
548 if(gShouldStop)
|
andrewm@0
|
549 break;
|
andrewm@0
|
550
|
andrewm@0
|
551 if(xenomai_gpio != 0) {
|
andrewm@0
|
552 // Set the test pin high
|
andrewm@0
|
553 xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN_MASK;
|
andrewm@0
|
554 }
|
andrewm@0
|
555
|
andrewm@0
|
556 // Render from/to buffer 1
|
andrewm@0
|
557
|
andrewm@0
|
558 // Convert short (16-bit) samples to float
|
andrewm@0
|
559 for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++)
|
andrewm@0
|
560 audioInBuffer[n] = (float)pru_buffer_audio_adc[n + audio_buffer_frames * 2] / 32768.0;
|
andrewm@0
|
561
|
giuliomoro@16
|
562 if(spi_enabled) {
|
giuliomoro@23
|
563 //convert input values TODO: move to PRU
|
giuliomoro@23
|
564 for(unsigned int n = 0; n < spi_num_channels * spi_buffer_frames; n++){
|
giuliomoro@19
|
565 analogInBuffer[n] = (float)pru_buffer_spi_adc[n + spi_buffer_frames * spi_num_channels] / 65536.0;
|
giuliomoro@23
|
566 }
|
giuliomoro@23
|
567 //initialize the output buffer with the values that were in the last frame of the previous output
|
giuliomoro@23
|
568 for(int n = 0; n < spi_num_channels; n++){
|
giuliomoro@23
|
569 for(unsigned int j = 0; j < spi_buffer_frames; j++){
|
giuliomoro@23
|
570 analogOutBuffer[j*spi_buffer_frames + n] = lastAnalogOutFrame[n];
|
giuliomoro@23
|
571 }
|
giuliomoro@23
|
572 }
|
giuliomoro@19
|
573 if(digital_enabled){
|
giuliomoro@23
|
574 for(unsigned int n = 0; n < digital_buffer_frames; n++){
|
giuliomoro@23
|
575 uint16_t inputs=lastDigitalBuffer[n]&0xffff;//half-word, has 1 for inputs and 0 for outputs
|
giuliomoro@16
|
576 uint16_t outputs=~inputs; //half-word has 1 for outputs and one for inputs;
|
giuliomoro@23
|
577 digitalBuffer1[n]=(lastDigitalBuffer[digital_buffer_frames-1]&(outputs<<16))| //keep output values set in the last frame of the previous buffer
|
giuliomoro@19
|
578 (digitalBuffer1[n]&(inputs<<16)) | //inputs from current digitalBuffer1[n];
|
giuliomoro@23
|
579 (lastDigitalBuffer[n]&(inputs)); //keep pin configuration from previous digitalBuffer1[n]
|
giuliomoro@19
|
580 // digitalBuffer1[n]=digitalBufferTemp[n]; //ignores inputs
|
giuliomoro@16
|
581 }
|
giuliomoro@16
|
582 }
|
giuliomoro@19
|
583 render(spi_buffer_frames, digital_buffer_frames, audio_buffer_frames, audioInBuffer, audioOutBuffer,
|
giuliomoro@19
|
584 analogInBuffer, analogOutBuffer, digitalBuffer1);
|
giuliomoro@23
|
585 //remember the content of the lastAnalogOutFrame
|
giuliomoro@23
|
586 for(int n = 0; n < spi_num_channels; n++){
|
giuliomoro@23
|
587 lastAnalogOutFrame[n] = analogOutBuffer[spi_buffer_frames*(spi_buffer_frames-1) + n];
|
giuliomoro@23
|
588 }
|
giuliomoro@23
|
589
|
giuliomoro@16
|
590 for(unsigned int n = 0; n < spi_num_channels * spi_buffer_frames; n++) {
|
giuliomoro@19
|
591 int out = analogOutBuffer[n] * 65536.0;
|
giuliomoro@16
|
592 if(out < 0) out = 0;
|
giuliomoro@16
|
593 else if(out > 65535) out = 65535;
|
giuliomoro@16
|
594 pru_buffer_spi_dac[n + spi_buffer_frames * spi_num_channels] = (uint16_t)out;
|
giuliomoro@16
|
595 }
|
giuliomoro@19
|
596 if(digital_enabled){ // keep track of past digital values
|
giuliomoro@19
|
597 for(unsigned int n = 0; n < digital_buffer_frames; n++){
|
giuliomoro@23
|
598 lastDigitalBuffer[n]=digitalBuffer1[n];
|
giuliomoro@16
|
599 }
|
giuliomoro@16
|
600 }
|
giuliomoro@16
|
601 }
|
andrewm@0
|
602 else
|
giuliomoro@19
|
603 render(0, 0, audio_buffer_frames, audioInBuffer, audioOutBuffer, 0, 0, 0); // we still pass digitalBuffer, just it is unused
|
andrewm@0
|
604
|
andrewm@0
|
605 // Convert float back to short
|
andrewm@0
|
606 for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++) {
|
andrewm@0
|
607 int out = audioOutBuffer[n] * 32768.0;
|
andrewm@0
|
608 if(out < -32768) out = -32768;
|
andrewm@0
|
609 else if(out > 32767) out = 32767;
|
andrewm@0
|
610 pru_buffer_audio_dac[n + audio_buffer_frames * 2] = (int16_t)out;
|
andrewm@0
|
611 }
|
andrewm@0
|
612
|
andrewm@0
|
613 if(xenomai_gpio != 0) {
|
andrewm@0
|
614 // Set the test pin high
|
andrewm@0
|
615 xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN_MASK;
|
andrewm@0
|
616 }
|
andrewm@0
|
617 }
|
andrewm@0
|
618
|
andrewm@0
|
619 // Tell PRU to stop
|
andrewm@0
|
620 pru_buffer_comm[PRU_SHOULD_STOP] = 1;
|
andrewm@0
|
621
|
giuliomoro@19
|
622 free(analogOutBuffer);
|
andrewm@0
|
623 free(audioInBuffer);
|
andrewm@0
|
624 free(audioOutBuffer);
|
giuliomoro@19
|
625 free(analogInBuffer);
|
giuliomoro@23
|
626 free(lastAnalogOutFrame);
|
giuliomoro@23
|
627 free(lastDigitalBuffer);
|
andrewm@0
|
628 }
|
andrewm@0
|
629
|
andrewm@0
|
630 // Wait for an interrupt from the PRU indicate it is finished
|
andrewm@0
|
631 void PRU::waitForFinish()
|
andrewm@0
|
632 {
|
andrewm@0
|
633 if(!running)
|
andrewm@0
|
634 return;
|
andrewm@15
|
635 prussdrv_pru_wait_event (pru_number == 0 ? PRU_EVTOUT_0 : PRU_EVTOUT_1);
|
andrewm@15
|
636 if(pru_number == 0)
|
andrewm@15
|
637 prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT);
|
andrewm@15
|
638 else
|
andrewm@15
|
639 prussdrv_pru_clear_event(PRU_EVTOUT_1, PRU1_ARM_INTERRUPT);
|
andrewm@0
|
640 }
|
andrewm@0
|
641
|
andrewm@0
|
642 // Turn off the PRU when done
|
andrewm@0
|
643 void PRU::disable()
|
andrewm@0
|
644 {
|
andrewm@0
|
645 /* Disable PRU and close memory mapping*/
|
andrewm@0
|
646 prussdrv_pru_disable(pru_number);
|
andrewm@0
|
647 prussdrv_exit();
|
andrewm@0
|
648 running = false;
|
andrewm@0
|
649 }
|
andrewm@0
|
650
|
andrewm@0
|
651 // Debugging
|
andrewm@0
|
652 void PRU::setGPIOTestPin()
|
andrewm@0
|
653 {
|
andrewm@0
|
654 if(!xenomai_gpio)
|
andrewm@0
|
655 return;
|
andrewm@0
|
656 xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN2_MASK;
|
andrewm@0
|
657 }
|
andrewm@0
|
658
|
andrewm@0
|
659 void PRU::clearGPIOTestPin()
|
andrewm@0
|
660 {
|
andrewm@0
|
661 if(!xenomai_gpio)
|
andrewm@0
|
662 return;
|
andrewm@0
|
663 xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN2_MASK;
|
andrewm@0
|
664 }
|