annotate core/PRU.cpp @ 269:ac8eb07afcf5

Oxygen text added to each render.cpp file for the default projects. Text includes project explanation from Wiki, edited in places. Empty project added as a default project. Doxyfile updated. Each of the project locations added to INPUT configuration option. Consider just watching the whole project file so all new projects are automatically pulled through.
author Robert Jack <robert.h.jack@gmail.com>
date Tue, 17 May 2016 15:40:16 +0100
parents c0bf6157f67e
children 33e0e4831763 88cf310417cd
rev   line source
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@45 21 #include "../include/BeagleRT.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
giuliomoro@231 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@45 106 PRU::PRU(BeagleRTContext *input_context)
andrewm@45 107 : context(input_context), pru_number(0), running(false), analog_enabled(false),
andrewm@45 108 digital_enabled(false), gpio_enabled(false), led_enabled(false),
andrewm@45 109 gpio_test_pin_enabled(false),
andrewm@45 110 pru_buffer_comm(0), pru_buffer_spi_dac(0), pru_buffer_spi_adc(0),
andrewm@45 111 pru_buffer_digital(0), pru_buffer_audio_dac(0), pru_buffer_audio_adc(0),
andrewm@45 112 xenomai_gpio_fd(-1), xenomai_gpio(0)
andrewm@0 113 {
andrewm@0 114
andrewm@0 115 }
andrewm@0 116
andrewm@0 117 // Destructor
andrewm@0 118 PRU::~PRU()
andrewm@0 119 {
andrewm@0 120 if(running)
andrewm@0 121 disable();
andrewm@0 122 if(gpio_enabled)
andrewm@0 123 cleanupGPIO();
andrewm@0 124 if(xenomai_gpio_fd >= 0)
andrewm@0 125 close(xenomai_gpio_fd);
andrewm@0 126 }
andrewm@0 127
andrewm@0 128 // Prepare the GPIO pins needed for the PRU
andrewm@0 129 // If include_test_pin is set, the GPIO output
andrewm@0 130 // is also prepared for an output which can be
andrewm@0 131 // viewed on a scope. If include_led is set,
andrewm@0 132 // user LED 3 on the BBB is taken over by the PRU
andrewm@0 133 // to indicate activity
andrewm@45 134 int PRU::prepareGPIO(int include_test_pin, int include_led)
andrewm@0 135 {
andrewm@45 136 if(context->analogFrames != 0) {
andrewm@0 137 // Prepare DAC CS/ pin: output, high to begin
andrewm@0 138 if(gpio_export(kPruGPIODACSyncPin)) {
andrewm@0 139 if(gRTAudioVerbose)
andrewm@0 140 cout << "Warning: couldn't export DAC sync pin\n";
andrewm@0 141 }
andrewm@0 142 if(gpio_set_dir(kPruGPIODACSyncPin, OUTPUT_PIN)) {
andrewm@0 143 if(gRTAudioVerbose)
andrewm@0 144 cout << "Couldn't set direction on DAC sync pin\n";
andrewm@0 145 return -1;
andrewm@0 146 }
andrewm@0 147 if(gpio_set_value(kPruGPIODACSyncPin, HIGH)) {
andrewm@0 148 if(gRTAudioVerbose)
andrewm@0 149 cout << "Couldn't set value on DAC sync pin\n";
andrewm@0 150 return -1;
andrewm@0 151 }
andrewm@0 152
andrewm@0 153 // Prepare ADC CS/ pin: output, high to begin
andrewm@0 154 if(gpio_export(kPruGPIOADCSyncPin)) {
andrewm@0 155 if(gRTAudioVerbose)
andrewm@0 156 cout << "Warning: couldn't export ADC sync pin\n";
andrewm@0 157 }
andrewm@0 158 if(gpio_set_dir(kPruGPIOADCSyncPin, OUTPUT_PIN)) {
andrewm@0 159 if(gRTAudioVerbose)
andrewm@0 160 cout << "Couldn't set direction on ADC sync pin\n";
andrewm@0 161 return -1;
andrewm@0 162 }
andrewm@0 163 if(gpio_set_value(kPruGPIOADCSyncPin, HIGH)) {
andrewm@0 164 if(gRTAudioVerbose)
andrewm@0 165 cout << "Couldn't set value on ADC sync pin\n";
andrewm@0 166 return -1;
andrewm@0 167 }
andrewm@0 168
andrewm@45 169 analog_enabled = true;
andrewm@0 170 }
andrewm@0 171
andrewm@45 172 if(context->digitalFrames != 0){
andrewm@45 173 for(unsigned int i = 0; i < context->digitalChannels; i++){
giuliomoro@19 174 if(gpio_export(digitalPins[i])) {
giuliomoro@16 175 if(gRTAudioVerbose)
giuliomoro@38 176 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 177 }
giuliomoro@38 178 if(gpio_set_dir(digitalPins[i], INPUT_PIN)) {
giuliomoro@16 179 if(gRTAudioVerbose)
giuliomoro@38 180 cerr << "Error: Couldn't set direction on digital GPIO pin " << digitalPins[i] << "\n";
giuliomoro@16 181 return -1;
giuliomoro@16 182 }
giuliomoro@16 183 }
andrewm@45 184 digital_enabled = true;
giuliomoro@16 185 }
giuliomoro@16 186
andrewm@0 187 if(include_test_pin) {
andrewm@0 188 // Prepare GPIO test output (for debugging), low to begin
andrewm@0 189 if(gpio_export(kPruGPIOTestPin)) {
andrewm@0 190 if(gRTAudioVerbose)
andrewm@0 191 cout << "Warning: couldn't export GPIO test pin\n";
andrewm@0 192 }
andrewm@0 193 if(gpio_set_dir(kPruGPIOTestPin, OUTPUT_PIN)) {
andrewm@0 194 if(gRTAudioVerbose)
andrewm@0 195 cout << "Couldn't set direction on GPIO test pin\n";
andrewm@0 196 return -1;
andrewm@0 197 }
andrewm@0 198 if(gpio_set_value(kPruGPIOTestPin, LOW)) {
andrewm@0 199 if(gRTAudioVerbose)
andrewm@0 200 cout << "Couldn't set value on GPIO test pin\n";
andrewm@0 201 return -1;
andrewm@0 202 }
andrewm@0 203
andrewm@0 204 if(gpio_export(kPruGPIOTestPin2)) {
andrewm@0 205 if(gRTAudioVerbose)
andrewm@0 206 cout << "Warning: couldn't export GPIO test pin 2\n";
andrewm@0 207 }
andrewm@0 208 if(gpio_set_dir(kPruGPIOTestPin2, OUTPUT_PIN)) {
andrewm@0 209 if(gRTAudioVerbose)
andrewm@0 210 cout << "Couldn't set direction on GPIO test pin 2\n";
andrewm@0 211 return -1;
andrewm@0 212 }
andrewm@0 213 if(gpio_set_value(kPruGPIOTestPin2, LOW)) {
andrewm@0 214 if(gRTAudioVerbose)
andrewm@0 215 cout << "Couldn't set value on GPIO test pin 2\n";
andrewm@0 216 return -1;
andrewm@0 217 }
andrewm@0 218
andrewm@0 219 if(gpio_export(kPruGPIOTestPin3)) {
andrewm@0 220 if(gRTAudioVerbose)
andrewm@0 221 cout << "Warning: couldn't export GPIO test pin 3\n";
andrewm@0 222 }
andrewm@0 223 if(gpio_set_dir(kPruGPIOTestPin3, OUTPUT_PIN)) {
andrewm@0 224 if(gRTAudioVerbose)
andrewm@0 225 cout << "Couldn't set direction on GPIO test pin 3\n";
andrewm@0 226 return -1;
andrewm@0 227 }
andrewm@0 228 if(gpio_set_value(kPruGPIOTestPin3, LOW)) {
andrewm@0 229 if(gRTAudioVerbose)
andrewm@0 230 cout << "Couldn't set value on GPIO test pin 3\n";
andrewm@0 231 return -1;
andrewm@0 232 }
andrewm@0 233 gpio_test_pin_enabled = true;
andrewm@0 234 }
andrewm@0 235
andrewm@0 236 if(include_led) {
andrewm@0 237 // Turn off system function for LED3 so it can be reused by PRU
andrewm@0 238 led_set_trigger(3, "none");
andrewm@0 239 led_enabled = true;
andrewm@0 240 }
andrewm@0 241
andrewm@0 242 gpio_enabled = true;
andrewm@0 243
andrewm@0 244 return 0;
andrewm@0 245 }
andrewm@0 246
andrewm@0 247 // Clean up the GPIO at the end
andrewm@0 248 void PRU::cleanupGPIO()
andrewm@0 249 {
andrewm@0 250 if(!gpio_enabled)
andrewm@0 251 return;
andrewm@45 252 if(analog_enabled) {
andrewm@0 253 gpio_unexport(kPruGPIODACSyncPin);
andrewm@0 254 gpio_unexport(kPruGPIOADCSyncPin);
andrewm@0 255 }
giuliomoro@19 256 if(digital_enabled){
andrewm@45 257 for(unsigned int i = 0; i < context->digitalChannels; i++){
giuliomoro@19 258 gpio_unexport(digitalPins[i]);
giuliomoro@16 259 }
giuliomoro@16 260 }
andrewm@0 261 if(gpio_test_pin_enabled) {
andrewm@0 262 gpio_unexport(kPruGPIOTestPin);
andrewm@0 263 gpio_unexport(kPruGPIOTestPin2);
andrewm@0 264 gpio_unexport(kPruGPIOTestPin3);
andrewm@0 265 }
andrewm@0 266 if(led_enabled) {
andrewm@0 267 // Set LED back to default eMMC status
andrewm@0 268 // TODO: make it go back to its actual value before this program,
andrewm@0 269 // rather than the system default
andrewm@0 270 led_set_trigger(3, "mmc1");
andrewm@0 271 }
andrewm@0 272 gpio_enabled = gpio_test_pin_enabled = false;
andrewm@0 273 }
andrewm@0 274
andrewm@0 275 // Initialise and open the PRU
andrewm@12 276 int PRU::initialise(int pru_num, int frames_per_buffer, int spi_channels, bool xenomai_test_pin)
andrewm@0 277 {
andrewm@0 278 uint32_t *pruMem = 0;
andrewm@0 279
andrewm@0 280 if(!gpio_enabled) {
andrewm@0 281 rt_printf("initialise() called before GPIO enabled\n");
andrewm@0 282 return 1;
andrewm@0 283 }
andrewm@0 284
andrewm@0 285 pru_number = pru_num;
andrewm@0 286
andrewm@0 287 /* Initialize structure used by prussdrv_pruintc_intc */
andrewm@0 288 /* PRUSS_INTC_INITDATA is found in pruss_intc_mapping.h */
andrewm@0 289 tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA;
andrewm@0 290
andrewm@0 291 /* Allocate and initialize memory */
andrewm@0 292 prussdrv_init();
andrewm@45 293 if(prussdrv_open(PRU_EVTOUT_0)) {
andrewm@0 294 rt_printf("Failed to open PRU driver\n");
andrewm@0 295 return 1;
andrewm@0 296 }
andrewm@0 297
andrewm@0 298 /* Map PRU's INTC */
andrewm@0 299 prussdrv_pruintc_init(&pruss_intc_initdata);
andrewm@0 300
andrewm@0 301 /* Map PRU memory to pointers */
andrewm@0 302 prussdrv_map_prumem (PRUSS0_SHARED_DATARAM, (void **)&pruMem);
andrewm@0 303 pru_buffer_comm = (uint32_t *)&pruMem[PRU_MEM_COMM_OFFSET/sizeof(uint32_t)];
andrewm@0 304 pru_buffer_audio_dac = (int16_t *)&pruMem[PRU_MEM_MCASP_OFFSET/sizeof(uint32_t)];
andrewm@0 305
andrewm@12 306 /* ADC memory starts 2(ch)*2(buffers)*bufsize samples later */
andrewm@45 307 pru_buffer_audio_adc = &pru_buffer_audio_dac[4 * context->audioFrames];
andrewm@0 308
andrewm@45 309 if(analog_enabled) {
andrewm@0 310 prussdrv_map_prumem (pru_number == 0 ? PRUSS0_PRU0_DATARAM : PRUSS0_PRU1_DATARAM, (void **)&pruMem);
andrewm@0 311 pru_buffer_spi_dac = (uint16_t *)&pruMem[PRU_MEM_DAC_OFFSET/sizeof(uint32_t)];
andrewm@0 312
andrewm@12 313 /* ADC memory starts after N(ch)*2(buffers)*bufsize samples */
andrewm@45 314 pru_buffer_spi_adc = &pru_buffer_spi_dac[2 * context->analogChannels * context->analogFrames];
andrewm@0 315 }
andrewm@0 316 else {
andrewm@0 317 pru_buffer_spi_dac = pru_buffer_spi_adc = 0;
andrewm@0 318 }
andrewm@0 319
giuliomoro@19 320 if(digital_enabled) {
giuliomoro@16 321 prussdrv_map_prumem (PRUSS0_SHARED_DATARAM, (void **)&pruMem);
giuliomoro@19 322 pru_buffer_digital = (uint32_t *)&pruMem[PRU_MEM_DIGITAL_OFFSET/sizeof(uint32_t)];
giuliomoro@16 323 }
giuliomoro@16 324 else {
giuliomoro@19 325 pru_buffer_digital = 0;
giuliomoro@16 326 }
andrewm@45 327
andrewm@0 328 /* Set up flags */
andrewm@0 329 pru_buffer_comm[PRU_SHOULD_STOP] = 0;
andrewm@0 330 pru_buffer_comm[PRU_CURRENT_BUFFER] = 0;
andrewm@45 331 pru_buffer_comm[PRU_BUFFER_FRAMES] = context->analogFrames;
andrewm@0 332 pru_buffer_comm[PRU_SHOULD_SYNC] = 0;
andrewm@0 333 pru_buffer_comm[PRU_SYNC_ADDRESS] = 0;
andrewm@0 334 pru_buffer_comm[PRU_SYNC_PIN_MASK] = 0;
andrewm@0 335 if(led_enabled) {
andrewm@0 336 pru_buffer_comm[PRU_LED_ADDRESS] = USERLED3_GPIO_BASE;
andrewm@0 337 pru_buffer_comm[PRU_LED_PIN_MASK] = USERLED3_PIN_MASK;
andrewm@0 338 }
andrewm@0 339 else {
andrewm@0 340 pru_buffer_comm[PRU_LED_ADDRESS] = 0;
andrewm@0 341 pru_buffer_comm[PRU_LED_PIN_MASK] = 0;
andrewm@0 342 }
andrewm@45 343 if(analog_enabled) {
andrewm@0 344 pru_buffer_comm[PRU_USE_SPI] = 1;
andrewm@45 345 pru_buffer_comm[PRU_SPI_NUM_CHANNELS] = context->analogChannels;
andrewm@0 346 }
andrewm@0 347 else {
andrewm@0 348 pru_buffer_comm[PRU_USE_SPI] = 0;
andrewm@12 349 pru_buffer_comm[PRU_SPI_NUM_CHANNELS] = 0;
andrewm@0 350 }
giuliomoro@19 351 if(digital_enabled) {
giuliomoro@38 352 pru_buffer_comm[PRU_USE_DIGITAL] = 1;
giuliomoro@38 353 //TODO: add mask
giuliomoro@16 354 }
giuliomoro@16 355 else {
giuliomoro@38 356 pru_buffer_comm[PRU_USE_DIGITAL] = 0;
giuliomoro@38 357
giuliomoro@16 358 }
andrewm@0 359
giuliomoro@38 360 /* Clear ADC and DAC memory.*/
giuliomoro@38 361 //TODO: this initialisation should only address the memory effectively used by these buffers, i.e.:depend on the number of frames
giuliomoro@38 362 // (otherwise might cause issues if we move memory locations later on)
andrewm@45 363 if(analog_enabled) {
andrewm@0 364 for(int i = 0; i < PRU_MEM_DAC_LENGTH / 2; i++)
andrewm@0 365 pru_buffer_spi_dac[i] = 0;
giuliomoro@38 366 if(digital_enabled){
giuliomoro@38 367 for(int i = 0; i < PRU_MEM_DIGITAL_OFFSET*2; i++)
giuliomoro@38 368 pru_buffer_digital[i] = 0x0000ffff; // set to all inputs, to avoid unexpected spikes
giuliomoro@38 369 }
andrewm@0 370 }
andrewm@0 371 for(int i = 0; i < PRU_MEM_MCASP_LENGTH / 2; i++)
andrewm@0 372 pru_buffer_audio_dac[i] = 0;
andrewm@45 373
andrewm@0 374 /* If using GPIO test pin for Xenomai (for debugging), initialise the pointer now */
andrewm@0 375 if(xenomai_test_pin && xenomai_gpio_fd < 0) {
andrewm@0 376 xenomai_gpio_fd = open("/dev/mem", O_RDWR);
andrewm@0 377 if(xenomai_gpio_fd < 0)
andrewm@0 378 rt_printf("Unable to open /dev/mem for GPIO test pin\n");
andrewm@0 379 else {
andrewm@0 380 xenomai_gpio = (uint32_t *)mmap(0, GPIO_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, xenomai_gpio_fd, TEST_PIN_GPIO_BASE);
andrewm@0 381 if(xenomai_gpio == MAP_FAILED) {
andrewm@0 382 rt_printf("Unable to map GPIO address for test pin\n");
andrewm@0 383 xenomai_gpio = 0;
andrewm@0 384 close(xenomai_gpio_fd);
andrewm@0 385 xenomai_gpio_fd = -1;
andrewm@0 386 }
andrewm@0 387 }
andrewm@0 388 }
andrewm@0 389
andrewm@81 390 // Allocate audio buffers
andrewm@81 391 context->audioIn = (float *)malloc(2 * context->audioFrames * sizeof(float));
andrewm@81 392 context->audioOut = (float *)malloc(2 * context->audioFrames * sizeof(float));
andrewm@81 393 if(context->audioIn == 0 || context->audioOut == 0) {
andrewm@81 394 rt_printf("Error: couldn't allocate audio buffers\n");
andrewm@81 395 return 1;
andrewm@81 396 }
andrewm@81 397
andrewm@81 398 // Allocate analog buffers
andrewm@81 399 if(analog_enabled) {
andrewm@81 400 context->analogIn = (float *)malloc(context->analogChannels * context->analogFrames * sizeof(float));
andrewm@81 401 context->analogOut = (float *)malloc(context->analogChannels * context->analogFrames * sizeof(float));
andrewm@81 402 last_analog_out_frame = (float *)malloc(context->analogChannels * sizeof(float));
andrewm@81 403
andrewm@81 404 if(context->analogIn == 0 || context->analogOut == 0 || last_analog_out_frame == 0) {
andrewm@81 405 rt_printf("Error: couldn't allocate analog buffers\n");
andrewm@81 406 return 1;
andrewm@81 407 }
andrewm@81 408
andrewm@81 409 memset(last_analog_out_frame, 0, context->analogChannels * sizeof(float));
andrewm@81 410 }
andrewm@81 411
andrewm@81 412 // Allocate digital buffers
andrewm@81 413 digital_buffer0 = pru_buffer_digital;
andrewm@81 414 digital_buffer1 = pru_buffer_digital + MEM_DIGITAL_BUFFER1_OFFSET / sizeof(uint32_t);
andrewm@81 415 if(digital_enabled) {
andrewm@81 416 last_digital_buffer = (uint32_t *)malloc(context->digitalFrames * sizeof(uint32_t)); //temp buffer to hold previous states
andrewm@81 417 if(last_digital_buffer == 0) {
andrewm@81 418 rt_printf("Error: couldn't allocate digital buffers\n");
andrewm@81 419 return 1;
andrewm@81 420 }
andrewm@81 421
andrewm@81 422 for(unsigned int n = 0; n < context->digitalFrames; n++){
andrewm@81 423 // Initialize lastDigitalFrames to all inputs
andrewm@81 424 last_digital_buffer[n] = 0x0000ffff;
andrewm@81 425 }
andrewm@81 426 }
andrewm@81 427
andrewm@81 428 context->digital = digital_buffer0;
andrewm@81 429
andrewm@0 430 return 0;
andrewm@0 431 }
andrewm@0 432
andrewm@0 433 // Run the code image in the specified file
giuliomoro@16 434 int PRU::start(char * const filename)
andrewm@0 435 {
andrewm@0 436 /* Clear any old interrupt */
andrewm@45 437 prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT);
andrewm@45 438
giuliomoro@16 439 /* Load and execute binary on PRU */
giuliomoro@16 440 if(filename[0] == '\0') { //if the string is empty, load the embedded code
giuliomoro@16 441 if(gRTAudioVerbose)
giuliomoro@16 442 rt_printf("Using embedded PRU code\n");
giuliomoro@16 443 if(prussdrv_exec_code(pru_number, PRUcode, sizeof(PRUcode))) {
giuliomoro@16 444 rt_printf("Failed to execute PRU code\n");
giuliomoro@16 445 return 1;
giuliomoro@16 446 }
giuliomoro@16 447 } else {
giuliomoro@16 448 if(gRTAudioVerbose)
giuliomoro@16 449 rt_printf("Using PRU code from %s\n",filename);
giuliomoro@16 450 if(prussdrv_exec_program(pru_number, filename)) {
giuliomoro@16 451 rt_printf("Failed to execute PRU code from %s\n", filename);
giuliomoro@16 452 return 1;
giuliomoro@16 453 }
giuliomoro@16 454 }
andrewm@0 455
andrewm@0 456 running = true;
andrewm@0 457 return 0;
andrewm@0 458 }
andrewm@0 459
andrewm@0 460 // Main loop to read and write data from/to PRU
andrewm@45 461 void PRU::loop(RT_INTR *pru_interrupt, void *userData)
andrewm@0 462 {
andrewm@50 463 #ifdef BEAGLERT_USE_XENOMAI_INTERRUPTS
andrewm@50 464 RTIME irqTimeout = PRU_SAMPLE_INTERVAL_NS * 1024; // Timeout for PRU interrupt: about 10ms, much longer than any expected period
andrewm@50 465 #else
andrewm@0 466 // Polling interval is 1/4 of the period
andrewm@50 467 RTIME sleepTime = PRU_SAMPLE_INTERVAL_NS * (context->analogChannels / 2) * context->analogFrames / 4;
andrewm@50 468 #endif
andrewm@45 469
andrewm@45 470 uint32_t pru_audio_offset, pru_spi_offset;
andrewm@0 471
andrewm@81 472 // Before starting, look at the last state of the analog and digital outputs which might
andrewm@81 473 // have been changed by the user during the setup() function. This lets us start with pin
andrewm@81 474 // directions and output values at something other than defaults.
andrewm@81 475
andrewm@81 476 if(analog_enabled) {
andrewm@81 477 if(context->flags & BEAGLERT_FLAG_ANALOG_OUTPUTS_PERSIST) {
andrewm@81 478 // Remember the content of the last_analog_out_frame
andrewm@81 479 for(unsigned int ch = 0; ch < context->analogChannels; ch++){
andrewm@81 480 last_analog_out_frame[ch] = context->analogOut[context->analogChannels * (context->analogFrames - 1) + ch];
andrewm@81 481 }
andrewm@81 482 }
andrewm@0 483 }
andrewm@45 484
andrewm@45 485 if(digital_enabled) {
andrewm@45 486 for(unsigned int n = 0; n < context->digitalFrames; n++){
andrewm@81 487 last_digital_buffer[n] = context->digital[n];
andrewm@45 488 }
giuliomoro@38 489 }
andrewm@45 490
andrewm@45 491 // TESTING
andrewm@50 492 // uint32_t testCount = 0;
andrewm@45 493 // RTIME startTime = rt_timer_read();
andrewm@45 494
andrewm@56 495 #ifdef BEAGLERT_USE_XENOMAI_INTERRUPTS
andrewm@56 496 int result;
andrewm@56 497 #else
andrewm@50 498 // Which buffer the PRU was last processing
andrewm@50 499 uint32_t lastPRUBuffer = 0;
andrewm@50 500 #endif
andrewm@50 501
andrewm@0 502 while(!gShouldStop) {
andrewm@50 503 #ifdef BEAGLERT_USE_XENOMAI_INTERRUPTS
andrewm@45 504 // Wait for PRU to move to change buffers;
andrewm@45 505 // PRU will send an interrupts which we wait for
andrewm@45 506 rt_intr_enable(pru_interrupt);
andrewm@45 507 while(!gShouldStop) {
andrewm@45 508 result = rt_intr_wait(pru_interrupt, irqTimeout);
andrewm@45 509 if(result >= 0)
andrewm@45 510 break;
andrewm@45 511 else if(result == -ETIMEDOUT)
andrewm@45 512 rt_printf("Warning: PRU timeout!\n");
andrewm@45 513 else {
andrewm@45 514 rt_printf("Error: wait for interrupt failed (%d)\n", result);
andrewm@45 515 gShouldStop = 1;
andrewm@45 516 }
andrewm@0 517 }
andrewm@45 518
andrewm@45 519 // Clear pending PRU interrupt
andrewm@45 520 prussdrv_pru_clear_event(PRU_EVTOUT_1, PRU1_ARM_INTERRUPT);
andrewm@50 521 #else
andrewm@50 522 // Poll
andrewm@50 523 while(pru_buffer_comm[PRU_CURRENT_BUFFER] == lastPRUBuffer && !gShouldStop) {
andrewm@50 524 rt_task_sleep(sleepTime);
andrewm@50 525 }
andrewm@50 526
andrewm@50 527 lastPRUBuffer = pru_buffer_comm[PRU_CURRENT_BUFFER];
andrewm@50 528 #endif
andrewm@45 529
andrewm@0 530 if(gShouldStop)
andrewm@0 531 break;
andrewm@0 532
andrewm@45 533 // Check which buffer we're on-- will have been set right
andrewm@45 534 // before the interrupt was asserted
andrewm@45 535 if(pru_buffer_comm[PRU_CURRENT_BUFFER] == 1) {
andrewm@45 536 // PRU is on buffer 1. We read and write to buffer 0
andrewm@45 537 pru_audio_offset = 0;
andrewm@45 538 pru_spi_offset = 0;
andrewm@45 539 if(digital_enabled)
andrewm@81 540 context->digital = digital_buffer0;
andrewm@45 541 }
andrewm@45 542 else {
andrewm@45 543 // PRU is on buffer 0. We read and write to buffer 1
andrewm@45 544 pru_audio_offset = context->audioFrames * 2;
andrewm@45 545 pru_spi_offset = context->analogFrames * context->analogChannels;
andrewm@45 546 if(digital_enabled)
andrewm@81 547 context->digital = digital_buffer1;
andrewm@45 548 }
andrewm@45 549
andrewm@45 550 // FIXME: some sort of margin is needed here to prevent the audio
andrewm@45 551 // code from completely eating the Linux system
andrewm@50 552 // testCount++;
andrewm@45 553 //rt_task_sleep(sleepTime*4);
andrewm@45 554 //rt_task_sleep(sleepTime/4);
andrewm@45 555
andrewm@0 556 if(xenomai_gpio != 0) {
andrewm@0 557 // Set the test pin high
andrewm@0 558 xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN_MASK;
andrewm@0 559 }
andrewm@0 560
andrewm@45 561 // Convert short (16-bit) samples to float
andrewm@45 562 // TODO: NEON
andrewm@45 563 for(unsigned int n = 0; n < 2 * context->audioFrames; n++)
giuliomoro@231 564 context->audioIn[n] = (float)pru_buffer_audio_adc[n + pru_audio_offset] / 32768.0f;
andrewm@0 565
andrewm@45 566 if(analog_enabled) {
andrewm@45 567 // TODO: NEON
andrewm@45 568 for(unsigned int n = 0; n < context->analogChannels * context->analogFrames; n++)
giuliomoro@231 569 context->analogIn[n] = (float)pru_buffer_spi_adc[n + pru_spi_offset] / 65536.0f;
andrewm@45 570
andrewm@45 571 if(context->flags & BEAGLERT_FLAG_ANALOG_OUTPUTS_PERSIST) {
andrewm@45 572 // Initialize the output buffer with the values that were in the last frame of the previous output
andrewm@45 573 for(unsigned int ch = 0; ch < context->analogChannels; ch++){
andrewm@45 574 for(unsigned int n = 0; n < context->analogFrames; n++){
andrewm@81 575 context->analogOut[n * context->analogChannels + ch] = last_analog_out_frame[ch];
andrewm@45 576 }
giuliomoro@23 577 }
giuliomoro@23 578 }
andrewm@45 579 else {
andrewm@45 580 // Outputs are 0 unless set otherwise
andrewm@45 581 memset(context->analogOut, 0, context->analogChannels * context->analogFrames * sizeof(float));
giuliomoro@23 582 }
andrewm@45 583 }
andrewm@45 584
andrewm@45 585 if(digital_enabled){
andrewm@45 586 // Use past digital values to initialize the array properly.
andrewm@45 587 // For each frame:
andrewm@45 588 // - pins previously set as outputs will keep the output value they had in the last frame of the previous buffer,
andrewm@45 589 // - pins previously set as inputs will carry the newly read input value
andrewm@45 590
andrewm@45 591 for(unsigned int n = 0; n < context->digitalFrames; n++){
andrewm@81 592 uint16_t inputs = last_digital_buffer[n] & 0xffff; // half-word, has 1 for inputs and 0 for outputs
andrewm@45 593
andrewm@45 594 uint16_t outputs = ~inputs; // half-word has 1 for outputs and 0 for inputs;
andrewm@81 595 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 596 (context->digital[n] & (inputs << 16)) | // inputs from current context->digital[n];
andrewm@81 597 (last_digital_buffer[n] & (inputs)); // keep pin configuration from previous context->digital[n]
andrewm@45 598 // context->digital[n]=digitalBufferTemp[n]; //ignores inputs
andrewm@45 599 }
andrewm@45 600 }
andrewm@45 601
andrewm@45 602 // Call user render function
andrewm@45 603 // ***********************
andrewm@45 604 render(context, userData);
andrewm@45 605 // ***********************
andrewm@45 606
andrewm@45 607 if(analog_enabled) {
andrewm@45 608 if(context->flags & BEAGLERT_FLAG_ANALOG_OUTPUTS_PERSIST) {
andrewm@81 609 // Remember the content of the last_analog_out_frame
andrewm@45 610 for(unsigned int ch = 0; ch < context->analogChannels; ch++){
andrewm@81 611 last_analog_out_frame[ch] = context->analogOut[context->analogChannels * (context->analogFrames - 1) + ch];
andrewm@45 612 }
andrewm@45 613 }
andrewm@45 614
andrewm@45 615 // Convert float back to short for SPI output
andrewm@45 616 for(unsigned int n = 0; n < context->analogChannels * context->analogFrames; n++) {
giuliomoro@231 617 int out = context->analogOut[n] * 65536.0f;
giuliomoro@16 618 if(out < 0) out = 0;
giuliomoro@16 619 else if(out > 65535) out = 65535;
andrewm@45 620 pru_buffer_spi_dac[n + pru_spi_offset] = (uint16_t)out;
giuliomoro@16 621 }
giuliomoro@16 622 }
andrewm@45 623
andrewm@45 624 if(digital_enabled) { // keep track of past digital values
andrewm@45 625 for(unsigned int n = 0; n < context->digitalFrames; n++){
andrewm@81 626 last_digital_buffer[n] = context->digital[n];
andrewm@45 627 }
andrewm@45 628 }
andrewm@45 629
andrewm@45 630 // Convert float back to short for audio
andrewm@45 631 // TODO: NEON
andrewm@45 632 for(unsigned int n = 0; n < 2 * context->audioFrames; n++) {
giuliomoro@231 633 int out = context->audioOut[n] * 32768.0f;
andrewm@0 634 if(out < -32768) out = -32768;
andrewm@0 635 else if(out > 32767) out = 32767;
andrewm@45 636 pru_buffer_audio_dac[n + pru_audio_offset] = (int16_t)out;
andrewm@0 637 }
andrewm@0 638
andrewm@52 639 // Increment total number of samples that have elapsed
andrewm@52 640 context->audioSampleCount += context->audioFrames;
andrewm@52 641
andrewm@0 642 if(xenomai_gpio != 0) {
andrewm@0 643 // Set the test pin high
andrewm@0 644 xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN_MASK;
andrewm@0 645 }
andrewm@0 646
andrewm@45 647 // FIXME: TESTING!!
andrewm@50 648 // if(testCount > 100000)
andrewm@50 649 // break;
andrewm@45 650 }
andrewm@0 651
andrewm@50 652 #ifdef BEAGLERT_USE_XENOMAI_INTERRUPTS
andrewm@45 653 // Turn off the interrupt for the PRU if it isn't already off
andrewm@45 654 rt_intr_disable(pru_interrupt);
andrewm@50 655 #endif
andrewm@0 656
andrewm@45 657 // FIXME: TESTING
andrewm@45 658 // RTIME endTime = rt_timer_read();
andrewm@45 659 // RTIME diffTime = endTime - startTime;
andrewm@45 660 // rt_printf("%d blocks elapsed in %f seconds, %f Hz block rate\n", testCount, ((float)diffTime / 1.0e9), (float)testCount / ((float)diffTime / 1.0e9));
andrewm@0 661
andrewm@0 662 // Tell PRU to stop
andrewm@0 663 pru_buffer_comm[PRU_SHOULD_STOP] = 1;
andrewm@0 664
andrewm@45 665 // Wait two buffer lengths for the PRU to finish
andrewm@45 666 rt_task_sleep(PRU_SAMPLE_INTERVAL_NS * context->analogFrames * 4 * 2);
andrewm@45 667
andrewm@45 668 // Clean up after ourselves
andrewm@45 669 free(context->audioIn);
andrewm@45 670 free(context->audioOut);
andrewm@45 671
andrewm@45 672 if(analog_enabled) {
andrewm@45 673 free(context->analogIn);
andrewm@45 674 free(context->analogOut);
andrewm@81 675 free(last_analog_out_frame);
andrewm@45 676 }
andrewm@45 677
andrewm@45 678 if(digital_enabled) {
andrewm@81 679 free(last_digital_buffer);
andrewm@45 680 }
andrewm@45 681
andrewm@45 682 context->audioIn = context->audioOut = 0;
andrewm@45 683 context->analogIn = context->analogOut = 0;
andrewm@45 684 context->digital = 0;
andrewm@0 685 }
andrewm@0 686
andrewm@0 687 // Wait for an interrupt from the PRU indicate it is finished
andrewm@0 688 void PRU::waitForFinish()
andrewm@0 689 {
andrewm@0 690 if(!running)
andrewm@0 691 return;
andrewm@45 692 prussdrv_pru_wait_event (PRU_EVTOUT_0);
andrewm@45 693 prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT);
andrewm@0 694 }
andrewm@0 695
andrewm@0 696 // Turn off the PRU when done
andrewm@0 697 void PRU::disable()
andrewm@0 698 {
andrewm@0 699 /* Disable PRU and close memory mapping*/
andrewm@0 700 prussdrv_pru_disable(pru_number);
andrewm@0 701 prussdrv_exit();
andrewm@0 702 running = false;
andrewm@0 703 }
andrewm@0 704
andrewm@0 705 // Debugging
andrewm@0 706 void PRU::setGPIOTestPin()
andrewm@0 707 {
andrewm@0 708 if(!xenomai_gpio)
andrewm@0 709 return;
andrewm@0 710 xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN2_MASK;
andrewm@0 711 }
andrewm@0 712
andrewm@0 713 void PRU::clearGPIOTestPin()
andrewm@0 714 {
andrewm@0 715 if(!xenomai_gpio)
andrewm@0 716 return;
andrewm@0 717 xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN2_MASK;
andrewm@0 718 }