annotate core/PRU.cpp @ 45:579c86316008 newapi

Major API overhaul. Moved to a single data structure for handling render functions. Functionally, generally similar except for scheduling within PRU loop function, which now uses interrupts from the PRU rather than polling. This requires an updated kernel.
author andrewm
date Thu, 28 May 2015 14:35:55 -0400
parents a9af130097e8
children be427da6fb9c
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
andrewm@45 102 extern bool 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@0 390 return 0;
andrewm@0 391 }
andrewm@0 392
andrewm@0 393 // Run the code image in the specified file
giuliomoro@16 394 int PRU::start(char * const filename)
andrewm@0 395 {
andrewm@0 396 /* Clear any old interrupt */
andrewm@45 397 prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT);
andrewm@45 398
giuliomoro@16 399 /* Load and execute binary on PRU */
giuliomoro@16 400 if(filename[0] == '\0') { //if the string is empty, load the embedded code
giuliomoro@16 401 if(gRTAudioVerbose)
giuliomoro@16 402 rt_printf("Using embedded PRU code\n");
giuliomoro@16 403 if(prussdrv_exec_code(pru_number, PRUcode, sizeof(PRUcode))) {
giuliomoro@16 404 rt_printf("Failed to execute PRU code\n");
giuliomoro@16 405 return 1;
giuliomoro@16 406 }
giuliomoro@16 407 } else {
giuliomoro@16 408 if(gRTAudioVerbose)
giuliomoro@16 409 rt_printf("Using PRU code from %s\n",filename);
giuliomoro@16 410 if(prussdrv_exec_program(pru_number, filename)) {
giuliomoro@16 411 rt_printf("Failed to execute PRU code from %s\n", filename);
giuliomoro@16 412 return 1;
giuliomoro@16 413 }
giuliomoro@16 414 }
andrewm@0 415
andrewm@0 416 running = true;
andrewm@0 417 return 0;
andrewm@0 418 }
andrewm@0 419
andrewm@0 420 // Main loop to read and write data from/to PRU
andrewm@45 421 void PRU::loop(RT_INTR *pru_interrupt, void *userData)
andrewm@0 422 {
andrewm@0 423 // Polling interval is 1/4 of the period
andrewm@45 424 //RTIME sleepTime = PRU_SAMPLE_INTERVAL_NS * (context->analogChannels / 2) * context->analogFrames / 4;
andrewm@45 425
andrewm@45 426 RTIME irqTimeout = PRU_SAMPLE_INTERVAL_NS * 1024; // Timeout for PRU interrupt: about 10ms, much longer than any expected period
andrewm@45 427 float *lastAnalogOutFrame;
giuliomoro@23 428 uint32_t *digitalBuffer0, *digitalBuffer1, *lastDigitalBuffer;
andrewm@45 429 uint32_t pru_audio_offset, pru_spi_offset;
andrewm@45 430 int result;
andrewm@0 431
andrewm@45 432 // Allocate audio buffers
andrewm@45 433 context->audioIn = (float *)malloc(2 * context->audioFrames * sizeof(float));
andrewm@45 434 context->audioOut = (float *)malloc(2 * context->audioFrames * sizeof(float));
andrewm@45 435 if(context->audioIn == 0 || context->audioOut == 0) {
giuliomoro@16 436 rt_printf("Error: couldn't allocate audio buffers\n");
andrewm@0 437 return;
andrewm@0 438 }
andrewm@45 439
andrewm@45 440 // Allocate analog buffers
andrewm@45 441 if(analog_enabled) {
andrewm@45 442 context->analogIn = (float *)malloc(context->analogChannels * context->analogFrames * sizeof(float));
andrewm@45 443 context->analogOut = (float *)malloc(context->analogChannels * context->analogFrames * sizeof(float));
andrewm@45 444 lastAnalogOutFrame = (float *)malloc(context->analogChannels * sizeof(float));
andrewm@45 445
andrewm@45 446 if(context->analogIn == 0 || context->analogOut == 0 || lastAnalogOutFrame == 0) {
andrewm@45 447 rt_printf("Error: couldn't allocate analog buffers\n");
andrewm@45 448 return;
andrewm@45 449 }
andrewm@45 450
andrewm@45 451 memset(lastAnalogOutFrame, 0, context->analogChannels * sizeof(float));
giuliomoro@23 452 }
giuliomoro@38 453
andrewm@45 454 // Allocate digital buffers
andrewm@45 455 digitalBuffer0 = pru_buffer_digital;
andrewm@45 456 digitalBuffer1 = pru_buffer_digital + MEM_DIGITAL_BUFFER1_OFFSET / sizeof(uint32_t);
andrewm@45 457 if(digital_enabled) {
andrewm@45 458 lastDigitalBuffer = (uint32_t *)malloc(context->digitalFrames * sizeof(uint32_t)); //temp buffer to hold previous states
andrewm@45 459 if(lastDigitalBuffer == 0) {
andrewm@45 460 rt_printf("Error: couldn't allocate digital buffers\n");
andrewm@45 461 return;
andrewm@45 462 }
andrewm@45 463
andrewm@45 464 for(unsigned int n = 0; n < context->digitalFrames; n++){
andrewm@45 465 // Initialize lastDigitalFrames to all inputs
andrewm@45 466 lastDigitalBuffer[n] = 0x0000ffff;
andrewm@45 467 }
giuliomoro@38 468 }
andrewm@45 469
andrewm@45 470 // TESTING
andrewm@45 471 uint32_t testCount = 0;
andrewm@45 472 // RTIME startTime = rt_timer_read();
andrewm@45 473
andrewm@0 474 while(!gShouldStop) {
andrewm@45 475 // Wait for PRU to move to change buffers;
andrewm@45 476 // PRU will send an interrupts which we wait for
andrewm@45 477 rt_intr_enable(pru_interrupt);
andrewm@45 478 while(!gShouldStop) {
andrewm@45 479 result = rt_intr_wait(pru_interrupt, irqTimeout);
andrewm@45 480 if(result >= 0)
andrewm@45 481 break;
andrewm@45 482 else if(result == -ETIMEDOUT)
andrewm@45 483 rt_printf("Warning: PRU timeout!\n");
andrewm@45 484 else {
andrewm@45 485 rt_printf("Error: wait for interrupt failed (%d)\n", result);
andrewm@45 486 gShouldStop = 1;
andrewm@45 487 }
andrewm@0 488 }
andrewm@45 489
andrewm@45 490 // Clear pending PRU interrupt
andrewm@45 491 prussdrv_pru_clear_event(PRU_EVTOUT_1, PRU1_ARM_INTERRUPT);
andrewm@45 492
andrewm@0 493 if(gShouldStop)
andrewm@0 494 break;
andrewm@0 495
andrewm@45 496 // Check which buffer we're on-- will have been set right
andrewm@45 497 // before the interrupt was asserted
andrewm@45 498 if(pru_buffer_comm[PRU_CURRENT_BUFFER] == 1) {
andrewm@45 499 // PRU is on buffer 1. We read and write to buffer 0
andrewm@45 500 pru_audio_offset = 0;
andrewm@45 501 pru_spi_offset = 0;
andrewm@45 502 if(digital_enabled)
andrewm@45 503 context->digital = digitalBuffer0;
andrewm@45 504 }
andrewm@45 505 else {
andrewm@45 506 // PRU is on buffer 0. We read and write to buffer 1
andrewm@45 507 pru_audio_offset = context->audioFrames * 2;
andrewm@45 508 pru_spi_offset = context->analogFrames * context->analogChannels;
andrewm@45 509 if(digital_enabled)
andrewm@45 510 context->digital = digitalBuffer1;
andrewm@45 511 }
andrewm@45 512
andrewm@45 513 // FIXME: some sort of margin is needed here to prevent the audio
andrewm@45 514 // code from completely eating the Linux system
andrewm@45 515 testCount++;
andrewm@45 516 //rt_task_sleep(sleepTime*4);
andrewm@45 517 //rt_task_sleep(sleepTime/4);
andrewm@45 518
andrewm@0 519 if(xenomai_gpio != 0) {
andrewm@0 520 // Set the test pin high
andrewm@0 521 xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN_MASK;
andrewm@0 522 }
andrewm@0 523
andrewm@45 524 // Convert short (16-bit) samples to float
andrewm@45 525 // TODO: NEON
andrewm@45 526 for(unsigned int n = 0; n < 2 * context->audioFrames; n++)
andrewm@45 527 context->audioIn[n] = (float)pru_buffer_audio_adc[n + pru_audio_offset] / 32768.0;
andrewm@0 528
andrewm@45 529 if(analog_enabled) {
andrewm@45 530 // TODO: NEON
andrewm@45 531 for(unsigned int n = 0; n < context->analogChannels * context->analogFrames; n++)
andrewm@45 532 context->analogIn[n] = (float)pru_buffer_spi_adc[n + pru_spi_offset] / 65536.0;
andrewm@45 533
andrewm@45 534 if(context->flags & BEAGLERT_FLAG_ANALOG_OUTPUTS_PERSIST) {
andrewm@45 535 // Initialize the output buffer with the values that were in the last frame of the previous output
andrewm@45 536 for(unsigned int ch = 0; ch < context->analogChannels; ch++){
andrewm@45 537 for(unsigned int n = 0; n < context->analogFrames; n++){
andrewm@45 538 context->analogOut[n * context->analogChannels + ch] = lastAnalogOutFrame[ch];
andrewm@45 539 }
giuliomoro@23 540 }
giuliomoro@23 541 }
andrewm@45 542 else {
andrewm@45 543 // Outputs are 0 unless set otherwise
andrewm@45 544 memset(context->analogOut, 0, context->analogChannels * context->analogFrames * sizeof(float));
giuliomoro@23 545 }
andrewm@45 546 }
andrewm@45 547
andrewm@45 548 if(digital_enabled){
andrewm@45 549 // Use past digital values to initialize the array properly.
andrewm@45 550 // For each frame:
andrewm@45 551 // - pins previously set as outputs will keep the output value they had in the last frame of the previous buffer,
andrewm@45 552 // - pins previously set as inputs will carry the newly read input value
andrewm@45 553
andrewm@45 554 for(unsigned int n = 0; n < context->digitalFrames; n++){
andrewm@45 555 uint16_t inputs = lastDigitalBuffer[n] & 0xffff; // half-word, has 1 for inputs and 0 for outputs
andrewm@45 556
andrewm@45 557 uint16_t outputs = ~inputs; // half-word has 1 for outputs and 0 for inputs;
andrewm@45 558 context->digital[n] = (lastDigitalBuffer[context->digitalFrames - 1] & (outputs << 16)) | // keep output values set in the last frame of the previous buffer
andrewm@45 559 (context->digital[n] & (inputs << 16)) | // inputs from current context->digital[n];
andrewm@45 560 (lastDigitalBuffer[n] & (inputs)); // keep pin configuration from previous context->digital[n]
andrewm@45 561 // context->digital[n]=digitalBufferTemp[n]; //ignores inputs
andrewm@45 562 }
andrewm@45 563 }
andrewm@45 564
andrewm@45 565 // Call user render function
andrewm@45 566 // ***********************
andrewm@45 567 render(context, userData);
andrewm@45 568 // ***********************
andrewm@45 569
andrewm@45 570 if(analog_enabled) {
andrewm@45 571 if(context->flags & BEAGLERT_FLAG_ANALOG_OUTPUTS_PERSIST) {
andrewm@45 572 // Remember the content of the lastAnalogOutFrame
andrewm@45 573 for(unsigned int ch = 0; ch < context->analogChannels; ch++){
andrewm@45 574 lastAnalogOutFrame[ch] = context->analogOut[context->analogChannels * (context->analogFrames - 1) + ch];
andrewm@45 575 }
andrewm@45 576 }
andrewm@45 577
andrewm@45 578 // Convert float back to short for SPI output
andrewm@45 579 for(unsigned int n = 0; n < context->analogChannels * context->analogFrames; n++) {
andrewm@45 580 int out = context->analogOut[n] * 65536.0;
giuliomoro@16 581 if(out < 0) out = 0;
giuliomoro@16 582 else if(out > 65535) out = 65535;
andrewm@45 583 pru_buffer_spi_dac[n + pru_spi_offset] = (uint16_t)out;
giuliomoro@16 584 }
giuliomoro@16 585 }
andrewm@45 586
andrewm@45 587 if(digital_enabled) { // keep track of past digital values
andrewm@45 588 for(unsigned int n = 0; n < context->digitalFrames; n++){
andrewm@45 589 lastDigitalBuffer[n] = context->digital[n];
andrewm@45 590 }
andrewm@45 591 }
andrewm@45 592
andrewm@45 593 // Convert float back to short for audio
andrewm@45 594 // TODO: NEON
andrewm@45 595 for(unsigned int n = 0; n < 2 * context->audioFrames; n++) {
andrewm@45 596 int out = context->audioOut[n] * 32768.0;
andrewm@0 597 if(out < -32768) out = -32768;
andrewm@0 598 else if(out > 32767) out = 32767;
andrewm@45 599 pru_buffer_audio_dac[n + pru_audio_offset] = (int16_t)out;
andrewm@0 600 }
andrewm@0 601
andrewm@0 602 if(xenomai_gpio != 0) {
andrewm@0 603 // Set the test pin high
andrewm@0 604 xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN_MASK;
andrewm@0 605 }
andrewm@0 606
andrewm@45 607 // FIXME: TESTING!!
andrewm@45 608 if(testCount > 100000)
andrewm@45 609 break;
andrewm@45 610 }
andrewm@0 611
andrewm@45 612 // Turn off the interrupt for the PRU if it isn't already off
andrewm@45 613 rt_intr_disable(pru_interrupt);
andrewm@0 614
andrewm@45 615 // FIXME: TESTING
andrewm@45 616 // RTIME endTime = rt_timer_read();
andrewm@45 617 // RTIME diffTime = endTime - startTime;
andrewm@45 618 // 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 619
andrewm@0 620 // Tell PRU to stop
andrewm@0 621 pru_buffer_comm[PRU_SHOULD_STOP] = 1;
andrewm@0 622
andrewm@45 623 // Wait two buffer lengths for the PRU to finish
andrewm@45 624 rt_task_sleep(PRU_SAMPLE_INTERVAL_NS * context->analogFrames * 4 * 2);
andrewm@45 625
andrewm@45 626 // Clean up after ourselves
andrewm@45 627 free(context->audioIn);
andrewm@45 628 free(context->audioOut);
andrewm@45 629
andrewm@45 630 if(analog_enabled) {
andrewm@45 631 free(context->analogIn);
andrewm@45 632 free(context->analogOut);
andrewm@45 633 free(lastAnalogOutFrame);
andrewm@45 634 }
andrewm@45 635
andrewm@45 636 if(digital_enabled) {
andrewm@45 637 free(lastDigitalBuffer);
andrewm@45 638 }
andrewm@45 639
andrewm@45 640 context->audioIn = context->audioOut = 0;
andrewm@45 641 context->analogIn = context->analogOut = 0;
andrewm@45 642 context->digital = 0;
andrewm@0 643 }
andrewm@0 644
andrewm@0 645 // Wait for an interrupt from the PRU indicate it is finished
andrewm@0 646 void PRU::waitForFinish()
andrewm@0 647 {
andrewm@0 648 if(!running)
andrewm@0 649 return;
andrewm@45 650 prussdrv_pru_wait_event (PRU_EVTOUT_0);
andrewm@45 651 prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT);
andrewm@0 652 }
andrewm@0 653
andrewm@0 654 // Turn off the PRU when done
andrewm@0 655 void PRU::disable()
andrewm@0 656 {
andrewm@0 657 /* Disable PRU and close memory mapping*/
andrewm@0 658 prussdrv_pru_disable(pru_number);
andrewm@0 659 prussdrv_exit();
andrewm@0 660 running = false;
andrewm@0 661 }
andrewm@0 662
andrewm@0 663 // Debugging
andrewm@0 664 void PRU::setGPIOTestPin()
andrewm@0 665 {
andrewm@0 666 if(!xenomai_gpio)
andrewm@0 667 return;
andrewm@0 668 xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN2_MASK;
andrewm@0 669 }
andrewm@0 670
andrewm@0 671 void PRU::clearGPIOTestPin()
andrewm@0 672 {
andrewm@0 673 if(!xenomai_gpio)
andrewm@0 674 return;
andrewm@0 675 xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN2_MASK;
andrewm@0 676 }