annotate core/PRU.cpp @ 56:3c3a1357657d newapi

Further API update to name three primary functions setup(), render() and cleanup(). Changed include paths so now can #include <BeagleRT.h>. Removed stale pru_rtaudio.bin file as this is now done as pru_rtaudio_bin.h. Updated examples to new API and fixed minor compiler warnings along the way. Network example needs further attention to compile.
author andrewm
date Wed, 15 Jul 2015 12:10:51 +0100
parents a6d223473ea2
children 92145ba7aabf
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@50 423 #ifdef BEAGLERT_USE_XENOMAI_INTERRUPTS
andrewm@50 424 RTIME irqTimeout = PRU_SAMPLE_INTERVAL_NS * 1024; // Timeout for PRU interrupt: about 10ms, much longer than any expected period
andrewm@50 425 #else
andrewm@0 426 // Polling interval is 1/4 of the period
andrewm@50 427 RTIME sleepTime = PRU_SAMPLE_INTERVAL_NS * (context->analogChannels / 2) * context->analogFrames / 4;
andrewm@50 428 #endif
andrewm@45 429
andrewm@45 430 float *lastAnalogOutFrame;
giuliomoro@23 431 uint32_t *digitalBuffer0, *digitalBuffer1, *lastDigitalBuffer;
andrewm@45 432 uint32_t pru_audio_offset, pru_spi_offset;
andrewm@0 433
andrewm@45 434 // Allocate audio buffers
andrewm@45 435 context->audioIn = (float *)malloc(2 * context->audioFrames * sizeof(float));
andrewm@45 436 context->audioOut = (float *)malloc(2 * context->audioFrames * sizeof(float));
andrewm@45 437 if(context->audioIn == 0 || context->audioOut == 0) {
giuliomoro@16 438 rt_printf("Error: couldn't allocate audio buffers\n");
andrewm@0 439 return;
andrewm@0 440 }
andrewm@45 441
andrewm@45 442 // Allocate analog buffers
andrewm@45 443 if(analog_enabled) {
andrewm@45 444 context->analogIn = (float *)malloc(context->analogChannels * context->analogFrames * sizeof(float));
andrewm@45 445 context->analogOut = (float *)malloc(context->analogChannels * context->analogFrames * sizeof(float));
andrewm@45 446 lastAnalogOutFrame = (float *)malloc(context->analogChannels * sizeof(float));
andrewm@45 447
andrewm@45 448 if(context->analogIn == 0 || context->analogOut == 0 || lastAnalogOutFrame == 0) {
andrewm@45 449 rt_printf("Error: couldn't allocate analog buffers\n");
andrewm@45 450 return;
andrewm@45 451 }
andrewm@45 452
andrewm@45 453 memset(lastAnalogOutFrame, 0, context->analogChannels * sizeof(float));
giuliomoro@23 454 }
giuliomoro@38 455
andrewm@45 456 // Allocate digital buffers
andrewm@45 457 digitalBuffer0 = pru_buffer_digital;
andrewm@45 458 digitalBuffer1 = pru_buffer_digital + MEM_DIGITAL_BUFFER1_OFFSET / sizeof(uint32_t);
andrewm@45 459 if(digital_enabled) {
andrewm@45 460 lastDigitalBuffer = (uint32_t *)malloc(context->digitalFrames * sizeof(uint32_t)); //temp buffer to hold previous states
andrewm@45 461 if(lastDigitalBuffer == 0) {
andrewm@45 462 rt_printf("Error: couldn't allocate digital buffers\n");
andrewm@45 463 return;
andrewm@45 464 }
andrewm@45 465
andrewm@45 466 for(unsigned int n = 0; n < context->digitalFrames; n++){
andrewm@45 467 // Initialize lastDigitalFrames to all inputs
andrewm@45 468 lastDigitalBuffer[n] = 0x0000ffff;
andrewm@45 469 }
giuliomoro@38 470 }
andrewm@45 471
andrewm@45 472 // TESTING
andrewm@50 473 // uint32_t testCount = 0;
andrewm@45 474 // RTIME startTime = rt_timer_read();
andrewm@45 475
andrewm@56 476 #ifdef BEAGLERT_USE_XENOMAI_INTERRUPTS
andrewm@56 477 int result;
andrewm@56 478 #else
andrewm@50 479 // Which buffer the PRU was last processing
andrewm@50 480 uint32_t lastPRUBuffer = 0;
andrewm@50 481 #endif
andrewm@50 482
andrewm@0 483 while(!gShouldStop) {
andrewm@50 484 #ifdef BEAGLERT_USE_XENOMAI_INTERRUPTS
andrewm@45 485 // Wait for PRU to move to change buffers;
andrewm@45 486 // PRU will send an interrupts which we wait for
andrewm@45 487 rt_intr_enable(pru_interrupt);
andrewm@45 488 while(!gShouldStop) {
andrewm@45 489 result = rt_intr_wait(pru_interrupt, irqTimeout);
andrewm@45 490 if(result >= 0)
andrewm@45 491 break;
andrewm@45 492 else if(result == -ETIMEDOUT)
andrewm@45 493 rt_printf("Warning: PRU timeout!\n");
andrewm@45 494 else {
andrewm@45 495 rt_printf("Error: wait for interrupt failed (%d)\n", result);
andrewm@45 496 gShouldStop = 1;
andrewm@45 497 }
andrewm@0 498 }
andrewm@45 499
andrewm@45 500 // Clear pending PRU interrupt
andrewm@45 501 prussdrv_pru_clear_event(PRU_EVTOUT_1, PRU1_ARM_INTERRUPT);
andrewm@50 502 #else
andrewm@50 503 // Poll
andrewm@50 504 while(pru_buffer_comm[PRU_CURRENT_BUFFER] == lastPRUBuffer && !gShouldStop) {
andrewm@50 505 rt_task_sleep(sleepTime);
andrewm@50 506 }
andrewm@50 507
andrewm@50 508 lastPRUBuffer = pru_buffer_comm[PRU_CURRENT_BUFFER];
andrewm@50 509 #endif
andrewm@45 510
andrewm@0 511 if(gShouldStop)
andrewm@0 512 break;
andrewm@0 513
andrewm@45 514 // Check which buffer we're on-- will have been set right
andrewm@45 515 // before the interrupt was asserted
andrewm@45 516 if(pru_buffer_comm[PRU_CURRENT_BUFFER] == 1) {
andrewm@45 517 // PRU is on buffer 1. We read and write to buffer 0
andrewm@45 518 pru_audio_offset = 0;
andrewm@45 519 pru_spi_offset = 0;
andrewm@45 520 if(digital_enabled)
andrewm@45 521 context->digital = digitalBuffer0;
andrewm@45 522 }
andrewm@45 523 else {
andrewm@45 524 // PRU is on buffer 0. We read and write to buffer 1
andrewm@45 525 pru_audio_offset = context->audioFrames * 2;
andrewm@45 526 pru_spi_offset = context->analogFrames * context->analogChannels;
andrewm@45 527 if(digital_enabled)
andrewm@45 528 context->digital = digitalBuffer1;
andrewm@45 529 }
andrewm@45 530
andrewm@45 531 // FIXME: some sort of margin is needed here to prevent the audio
andrewm@45 532 // code from completely eating the Linux system
andrewm@50 533 // testCount++;
andrewm@45 534 //rt_task_sleep(sleepTime*4);
andrewm@45 535 //rt_task_sleep(sleepTime/4);
andrewm@45 536
andrewm@0 537 if(xenomai_gpio != 0) {
andrewm@0 538 // Set the test pin high
andrewm@0 539 xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN_MASK;
andrewm@0 540 }
andrewm@0 541
andrewm@45 542 // Convert short (16-bit) samples to float
andrewm@45 543 // TODO: NEON
andrewm@45 544 for(unsigned int n = 0; n < 2 * context->audioFrames; n++)
andrewm@45 545 context->audioIn[n] = (float)pru_buffer_audio_adc[n + pru_audio_offset] / 32768.0;
andrewm@0 546
andrewm@45 547 if(analog_enabled) {
andrewm@45 548 // TODO: NEON
andrewm@45 549 for(unsigned int n = 0; n < context->analogChannels * context->analogFrames; n++)
andrewm@45 550 context->analogIn[n] = (float)pru_buffer_spi_adc[n + pru_spi_offset] / 65536.0;
andrewm@45 551
andrewm@45 552 if(context->flags & BEAGLERT_FLAG_ANALOG_OUTPUTS_PERSIST) {
andrewm@45 553 // Initialize the output buffer with the values that were in the last frame of the previous output
andrewm@45 554 for(unsigned int ch = 0; ch < context->analogChannels; ch++){
andrewm@45 555 for(unsigned int n = 0; n < context->analogFrames; n++){
andrewm@45 556 context->analogOut[n * context->analogChannels + ch] = lastAnalogOutFrame[ch];
andrewm@45 557 }
giuliomoro@23 558 }
giuliomoro@23 559 }
andrewm@45 560 else {
andrewm@45 561 // Outputs are 0 unless set otherwise
andrewm@45 562 memset(context->analogOut, 0, context->analogChannels * context->analogFrames * sizeof(float));
giuliomoro@23 563 }
andrewm@45 564 }
andrewm@45 565
andrewm@45 566 if(digital_enabled){
andrewm@45 567 // Use past digital values to initialize the array properly.
andrewm@45 568 // For each frame:
andrewm@45 569 // - pins previously set as outputs will keep the output value they had in the last frame of the previous buffer,
andrewm@45 570 // - pins previously set as inputs will carry the newly read input value
andrewm@45 571
andrewm@45 572 for(unsigned int n = 0; n < context->digitalFrames; n++){
andrewm@45 573 uint16_t inputs = lastDigitalBuffer[n] & 0xffff; // half-word, has 1 for inputs and 0 for outputs
andrewm@45 574
andrewm@45 575 uint16_t outputs = ~inputs; // half-word has 1 for outputs and 0 for inputs;
andrewm@45 576 context->digital[n] = (lastDigitalBuffer[context->digitalFrames - 1] & (outputs << 16)) | // keep output values set in the last frame of the previous buffer
andrewm@45 577 (context->digital[n] & (inputs << 16)) | // inputs from current context->digital[n];
andrewm@45 578 (lastDigitalBuffer[n] & (inputs)); // keep pin configuration from previous context->digital[n]
andrewm@45 579 // context->digital[n]=digitalBufferTemp[n]; //ignores inputs
andrewm@45 580 }
andrewm@45 581 }
andrewm@45 582
andrewm@45 583 // Call user render function
andrewm@45 584 // ***********************
andrewm@45 585 render(context, userData);
andrewm@45 586 // ***********************
andrewm@45 587
andrewm@45 588 if(analog_enabled) {
andrewm@45 589 if(context->flags & BEAGLERT_FLAG_ANALOG_OUTPUTS_PERSIST) {
andrewm@45 590 // Remember the content of the lastAnalogOutFrame
andrewm@45 591 for(unsigned int ch = 0; ch < context->analogChannels; ch++){
andrewm@45 592 lastAnalogOutFrame[ch] = context->analogOut[context->analogChannels * (context->analogFrames - 1) + ch];
andrewm@45 593 }
andrewm@45 594 }
andrewm@45 595
andrewm@45 596 // Convert float back to short for SPI output
andrewm@45 597 for(unsigned int n = 0; n < context->analogChannels * context->analogFrames; n++) {
andrewm@45 598 int out = context->analogOut[n] * 65536.0;
giuliomoro@16 599 if(out < 0) out = 0;
giuliomoro@16 600 else if(out > 65535) out = 65535;
andrewm@45 601 pru_buffer_spi_dac[n + pru_spi_offset] = (uint16_t)out;
giuliomoro@16 602 }
giuliomoro@16 603 }
andrewm@45 604
andrewm@45 605 if(digital_enabled) { // keep track of past digital values
andrewm@45 606 for(unsigned int n = 0; n < context->digitalFrames; n++){
andrewm@45 607 lastDigitalBuffer[n] = context->digital[n];
andrewm@45 608 }
andrewm@45 609 }
andrewm@45 610
andrewm@45 611 // Convert float back to short for audio
andrewm@45 612 // TODO: NEON
andrewm@45 613 for(unsigned int n = 0; n < 2 * context->audioFrames; n++) {
andrewm@45 614 int out = context->audioOut[n] * 32768.0;
andrewm@0 615 if(out < -32768) out = -32768;
andrewm@0 616 else if(out > 32767) out = 32767;
andrewm@45 617 pru_buffer_audio_dac[n + pru_audio_offset] = (int16_t)out;
andrewm@0 618 }
andrewm@0 619
andrewm@52 620 // Increment total number of samples that have elapsed
andrewm@52 621 context->audioSampleCount += context->audioFrames;
andrewm@52 622
andrewm@0 623 if(xenomai_gpio != 0) {
andrewm@0 624 // Set the test pin high
andrewm@0 625 xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN_MASK;
andrewm@0 626 }
andrewm@0 627
andrewm@45 628 // FIXME: TESTING!!
andrewm@50 629 // if(testCount > 100000)
andrewm@50 630 // break;
andrewm@45 631 }
andrewm@0 632
andrewm@50 633 #ifdef BEAGLERT_USE_XENOMAI_INTERRUPTS
andrewm@45 634 // Turn off the interrupt for the PRU if it isn't already off
andrewm@45 635 rt_intr_disable(pru_interrupt);
andrewm@50 636 #endif
andrewm@0 637
andrewm@45 638 // FIXME: TESTING
andrewm@45 639 // RTIME endTime = rt_timer_read();
andrewm@45 640 // RTIME diffTime = endTime - startTime;
andrewm@45 641 // 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 642
andrewm@0 643 // Tell PRU to stop
andrewm@0 644 pru_buffer_comm[PRU_SHOULD_STOP] = 1;
andrewm@0 645
andrewm@45 646 // Wait two buffer lengths for the PRU to finish
andrewm@45 647 rt_task_sleep(PRU_SAMPLE_INTERVAL_NS * context->analogFrames * 4 * 2);
andrewm@45 648
andrewm@45 649 // Clean up after ourselves
andrewm@45 650 free(context->audioIn);
andrewm@45 651 free(context->audioOut);
andrewm@45 652
andrewm@45 653 if(analog_enabled) {
andrewm@45 654 free(context->analogIn);
andrewm@45 655 free(context->analogOut);
andrewm@45 656 free(lastAnalogOutFrame);
andrewm@45 657 }
andrewm@45 658
andrewm@45 659 if(digital_enabled) {
andrewm@45 660 free(lastDigitalBuffer);
andrewm@45 661 }
andrewm@45 662
andrewm@45 663 context->audioIn = context->audioOut = 0;
andrewm@45 664 context->analogIn = context->analogOut = 0;
andrewm@45 665 context->digital = 0;
andrewm@0 666 }
andrewm@0 667
andrewm@0 668 // Wait for an interrupt from the PRU indicate it is finished
andrewm@0 669 void PRU::waitForFinish()
andrewm@0 670 {
andrewm@0 671 if(!running)
andrewm@0 672 return;
andrewm@45 673 prussdrv_pru_wait_event (PRU_EVTOUT_0);
andrewm@45 674 prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT);
andrewm@0 675 }
andrewm@0 676
andrewm@0 677 // Turn off the PRU when done
andrewm@0 678 void PRU::disable()
andrewm@0 679 {
andrewm@0 680 /* Disable PRU and close memory mapping*/
andrewm@0 681 prussdrv_pru_disable(pru_number);
andrewm@0 682 prussdrv_exit();
andrewm@0 683 running = false;
andrewm@0 684 }
andrewm@0 685
andrewm@0 686 // Debugging
andrewm@0 687 void PRU::setGPIOTestPin()
andrewm@0 688 {
andrewm@0 689 if(!xenomai_gpio)
andrewm@0 690 return;
andrewm@0 691 xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN2_MASK;
andrewm@0 692 }
andrewm@0 693
andrewm@0 694 void PRU::clearGPIOTestPin()
andrewm@0 695 {
andrewm@0 696 if(!xenomai_gpio)
andrewm@0 697 return;
andrewm@0 698 xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN2_MASK;
andrewm@0 699 }