annotate core/PRU.cpp @ 39:638bc1ae2500 staging

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