Mercurial > hg > beaglert
view core/PRU.cpp @ 0:8a575ba3ab52
Initial commit.
author | andrewm |
---|---|
date | Fri, 31 Oct 2014 19:10:17 +0100 |
parents | |
children | a6beeba3a648 |
line wrap: on
line source
/* * PRU.cpp * * Code for communicating with the Programmable Realtime Unit (PRU) * on the BeagleBone AM335x series processors. The PRU loads and runs * a separate code image compiled from an assembly file. Here it is * used to handle audio and SPI ADC/DAC data. * * This code is specific to the PRU code in the assembly file; for example, * it uses certain GPIO resources that correspond to that image. * * Created on: May 27, 2014 * Author: andrewm */ #include "../include/PRU.h" #include "../include/prussdrv.h" #include "../include/pruss_intc_mapping.h" #include "../include/GPIOcontrol.h" #include "../include/render.h" #include <iostream> #include <stdlib.h> #include <cstdio> #include <cerrno> #include <fcntl.h> #include <sys/mman.h> // Xenomai-specific includes #include <sys/mman.h> #include <native/task.h> #include <native/timer.h> #include <rtdk.h> using namespace std; #define PRU_MEM_MCASP_OFFSET 0x2000 // Offset within PRU-SHARED RAM #define PRU_MEM_MCASP_LENGTH 0x2000 // Length of McASP memory, in bytes #define PRU_MEM_DAC_OFFSET 0x0 // Offset within PRU0 RAM #define PRU_MEM_DAC_LENGTH 0x2000 // Length of ADC+DAC memory, in bytes #define PRU_MEM_COMM_OFFSET 0x0 // Offset within PRU-SHARED RAM #define PRU_SHOULD_STOP 0 #define PRU_CURRENT_BUFFER 1 #define PRU_BUFFER_FRAMES 2 #define PRU_SHOULD_SYNC 3 #define PRU_SYNC_ADDRESS 4 #define PRU_SYNC_PIN_MASK 5 #define PRU_LED_ADDRESS 6 #define PRU_LED_PIN_MASK 7 #define PRU_FRAME_COUNT 8 #define PRU_USE_SPI 9 #define PRU_SAMPLE_INTERVAL_NS 45351 // 22050Hz per SPI sample = 45.351us #define GPIO0_ADDRESS 0x44E07000 #define GPIO1_ADDRESS 0x4804C000 #define GPIO_SIZE 0x198 #define GPIO_CLEARDATAOUT (0x190 / 4) #define GPIO_SETDATAOUT (0x194 / 4) #define TEST_PIN_GPIO_BASE GPIO0_ADDRESS // Use GPIO0(31) for debugging #define TEST_PIN_MASK (1 << 31) #define TEST_PIN2_MASK (1 << 26) #define USERLED3_GPIO_BASE GPIO1_ADDRESS // GPIO1(24) is user LED 3 #define USERLED3_PIN_MASK (1 << 24) const unsigned int PRU::kPruGPIODACSyncPin = 5; // GPIO0(5); P9-17 const unsigned int PRU::kPruGPIOADCSyncPin = 48; // GPIO1(16); P9-15 const unsigned int PRU::kPruGPIOTestPin = 60; // GPIO1(28); P9-12 const unsigned int PRU::kPruGPIOTestPin2 = 31; // GPIO0(31); P9-13 const unsigned int PRU::kPruGPIOTestPin3 = 26; // GPIO0(26); P8-14 extern int gShouldStop; extern int gRTAudioVerbose; // Constructor: specify a PRU number (0 or 1) PRU::PRU() : pru_number(0), running(false), spi_enabled(false), gpio_enabled(false), led_enabled(false), gpio_test_pin_enabled(false), xenomai_gpio_fd(-1), xenomai_gpio(0) { } // Destructor PRU::~PRU() { if(running) disable(); if(gpio_enabled) cleanupGPIO(); if(xenomai_gpio_fd >= 0) close(xenomai_gpio_fd); } // Prepare the GPIO pins needed for the PRU // If include_test_pin is set, the GPIO output // is also prepared for an output which can be // viewed on a scope. If include_led is set, // user LED 3 on the BBB is taken over by the PRU // to indicate activity int PRU::prepareGPIO(int use_spi, int include_test_pin, int include_led) { if(use_spi) { // Prepare DAC CS/ pin: output, high to begin if(gpio_export(kPruGPIODACSyncPin)) { if(gRTAudioVerbose) cout << "Warning: couldn't export DAC sync pin\n"; } if(gpio_set_dir(kPruGPIODACSyncPin, OUTPUT_PIN)) { if(gRTAudioVerbose) cout << "Couldn't set direction on DAC sync pin\n"; return -1; } if(gpio_set_value(kPruGPIODACSyncPin, HIGH)) { if(gRTAudioVerbose) cout << "Couldn't set value on DAC sync pin\n"; return -1; } // Prepare ADC CS/ pin: output, high to begin if(gpio_export(kPruGPIOADCSyncPin)) { if(gRTAudioVerbose) cout << "Warning: couldn't export ADC sync pin\n"; } if(gpio_set_dir(kPruGPIOADCSyncPin, OUTPUT_PIN)) { if(gRTAudioVerbose) cout << "Couldn't set direction on ADC sync pin\n"; return -1; } if(gpio_set_value(kPruGPIOADCSyncPin, HIGH)) { if(gRTAudioVerbose) cout << "Couldn't set value on ADC sync pin\n"; return -1; } spi_enabled = true; } if(include_test_pin) { // Prepare GPIO test output (for debugging), low to begin if(gpio_export(kPruGPIOTestPin)) { if(gRTAudioVerbose) cout << "Warning: couldn't export GPIO test pin\n"; } if(gpio_set_dir(kPruGPIOTestPin, OUTPUT_PIN)) { if(gRTAudioVerbose) cout << "Couldn't set direction on GPIO test pin\n"; return -1; } if(gpio_set_value(kPruGPIOTestPin, LOW)) { if(gRTAudioVerbose) cout << "Couldn't set value on GPIO test pin\n"; return -1; } if(gpio_export(kPruGPIOTestPin2)) { if(gRTAudioVerbose) cout << "Warning: couldn't export GPIO test pin 2\n"; } if(gpio_set_dir(kPruGPIOTestPin2, OUTPUT_PIN)) { if(gRTAudioVerbose) cout << "Couldn't set direction on GPIO test pin 2\n"; return -1; } if(gpio_set_value(kPruGPIOTestPin2, LOW)) { if(gRTAudioVerbose) cout << "Couldn't set value on GPIO test pin 2\n"; return -1; } if(gpio_export(kPruGPIOTestPin3)) { if(gRTAudioVerbose) cout << "Warning: couldn't export GPIO test pin 3\n"; } if(gpio_set_dir(kPruGPIOTestPin3, OUTPUT_PIN)) { if(gRTAudioVerbose) cout << "Couldn't set direction on GPIO test pin 3\n"; return -1; } if(gpio_set_value(kPruGPIOTestPin3, LOW)) { if(gRTAudioVerbose) cout << "Couldn't set value on GPIO test pin 3\n"; return -1; } gpio_test_pin_enabled = true; } if(include_led) { // Turn off system function for LED3 so it can be reused by PRU led_set_trigger(3, "none"); led_enabled = true; } gpio_enabled = true; return 0; } // Clean up the GPIO at the end void PRU::cleanupGPIO() { if(!gpio_enabled) return; if(spi_enabled) { gpio_unexport(kPruGPIODACSyncPin); gpio_unexport(kPruGPIOADCSyncPin); } if(gpio_test_pin_enabled) { gpio_unexport(kPruGPIOTestPin); gpio_unexport(kPruGPIOTestPin2); gpio_unexport(kPruGPIOTestPin3); } if(led_enabled) { // Set LED back to default eMMC status // TODO: make it go back to its actual value before this program, // rather than the system default led_set_trigger(3, "mmc1"); } gpio_enabled = gpio_test_pin_enabled = false; } // Initialise and open the PRU int PRU::initialise(int pru_num, int frames_per_buffer, bool xenomai_test_pin) { uint32_t *pruMem = 0; if(!gpio_enabled) { rt_printf("initialise() called before GPIO enabled\n"); return 1; } pru_number = pru_num; /* Initialize structure used by prussdrv_pruintc_intc */ /* PRUSS_INTC_INITDATA is found in pruss_intc_mapping.h */ tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA; /* Allocate and initialize memory */ prussdrv_init(); if(prussdrv_open(PRU_EVTOUT_0)) { rt_printf("Failed to open PRU driver\n"); return 1; } /* Map PRU's INTC */ prussdrv_pruintc_init(&pruss_intc_initdata); spi_buffer_frames = frames_per_buffer; audio_buffer_frames = spi_buffer_frames * 2; /* Map PRU memory to pointers */ prussdrv_map_prumem (PRUSS0_SHARED_DATARAM, (void **)&pruMem); pru_buffer_comm = (uint32_t *)&pruMem[PRU_MEM_COMM_OFFSET/sizeof(uint32_t)]; pru_buffer_audio_dac = (int16_t *)&pruMem[PRU_MEM_MCASP_OFFSET/sizeof(uint32_t)]; /* ADC memory starts 2(ch)*2(buffers)*2(samples/spi)*bufsize samples later */ pru_buffer_audio_adc = &pru_buffer_audio_dac[8 * spi_buffer_frames]; if(spi_enabled) { prussdrv_map_prumem (pru_number == 0 ? PRUSS0_PRU0_DATARAM : PRUSS0_PRU1_DATARAM, (void **)&pruMem); pru_buffer_spi_dac = (uint16_t *)&pruMem[PRU_MEM_DAC_OFFSET/sizeof(uint32_t)]; /* ADC memory starts after 8(ch)*2(buffers)*bufsize samples */ pru_buffer_spi_adc = &pru_buffer_spi_dac[16 * spi_buffer_frames]; } else { pru_buffer_spi_dac = pru_buffer_spi_adc = 0; } /* Set up flags */ pru_buffer_comm[PRU_SHOULD_STOP] = 0; pru_buffer_comm[PRU_CURRENT_BUFFER] = 0; pru_buffer_comm[PRU_BUFFER_FRAMES] = spi_buffer_frames; pru_buffer_comm[PRU_SHOULD_SYNC] = 0; pru_buffer_comm[PRU_SYNC_ADDRESS] = 0; pru_buffer_comm[PRU_SYNC_PIN_MASK] = 0; if(led_enabled) { pru_buffer_comm[PRU_LED_ADDRESS] = USERLED3_GPIO_BASE; pru_buffer_comm[PRU_LED_PIN_MASK] = USERLED3_PIN_MASK; } else { pru_buffer_comm[PRU_LED_ADDRESS] = 0; pru_buffer_comm[PRU_LED_PIN_MASK] = 0; } if(spi_enabled) { pru_buffer_comm[PRU_USE_SPI] = 1; } else { pru_buffer_comm[PRU_USE_SPI] = 0; } /* Clear ADC and DAC memory */ if(spi_enabled) { for(int i = 0; i < PRU_MEM_DAC_LENGTH / 2; i++) pru_buffer_spi_dac[i] = 0; } for(int i = 0; i < PRU_MEM_MCASP_LENGTH / 2; i++) pru_buffer_audio_dac[i] = 0; /* If using GPIO test pin for Xenomai (for debugging), initialise the pointer now */ if(xenomai_test_pin && xenomai_gpio_fd < 0) { xenomai_gpio_fd = open("/dev/mem", O_RDWR); if(xenomai_gpio_fd < 0) rt_printf("Unable to open /dev/mem for GPIO test pin\n"); else { xenomai_gpio = (uint32_t *)mmap(0, GPIO_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, xenomai_gpio_fd, TEST_PIN_GPIO_BASE); if(xenomai_gpio == MAP_FAILED) { rt_printf("Unable to map GPIO address for test pin\n"); xenomai_gpio = 0; close(xenomai_gpio_fd); xenomai_gpio_fd = -1; } } } return 0; } // Run the code image in the specified file int PRU::start(char * const filename) { /* Clear any old interrupt */ prussdrv_pru_clear_event(pru_number == 0 ? PRU0_ARM_INTERRUPT : PRU1_ARM_INTERRUPT); /* Load and execute binary on PRU */ if(prussdrv_exec_program(pru_number, filename)) { rt_printf("Failed to execute PRU code from %s\n", filename); return 1; } running = true; return 0; } // Main loop to read and write data from/to PRU void PRU::loop() { // Polling interval is 1/4 of the period RTIME sleepTime = PRU_SAMPLE_INTERVAL_NS * spi_buffer_frames / 4; float *audioInBuffer, *audioOutBuffer; audioInBuffer = (float *)malloc(2 * audio_buffer_frames * sizeof(float)); audioOutBuffer = (float *)malloc(2 * audio_buffer_frames * sizeof(float)); if(audioInBuffer == 0 || audioOutBuffer == 0) { rt_printf("Error: couldn't allocated audio buffers\n"); return; } while(!gShouldStop) { // Wait for PRU to move to buffer 1 while(pru_buffer_comm[PRU_CURRENT_BUFFER] == 0 && !gShouldStop) { rt_task_sleep(sleepTime); } if(gShouldStop) break; if(xenomai_gpio != 0) { // Set the test pin high xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN_MASK; } // Render from/to buffer 0 // Convert short (16-bit) samples to float for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++) audioInBuffer[n] = (float)pru_buffer_audio_adc[n] / 32768.0; if(spi_enabled) render(spi_buffer_frames, audio_buffer_frames, audioInBuffer, audioOutBuffer, pru_buffer_spi_adc, pru_buffer_spi_dac); else render(0, audio_buffer_frames, audioInBuffer, audioOutBuffer, 0, 0); // Convert float back to short for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++) { int out = audioOutBuffer[n] * 32768.0; if(out < -32768) out = -32768; else if(out > 32767) out = 32767; pru_buffer_audio_dac[n] = (int16_t)out; } if(xenomai_gpio != 0) { // Set the test pin high xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN_MASK; } // Wait for PRU to move to buffer 0 while(pru_buffer_comm[PRU_CURRENT_BUFFER] != 0 && !gShouldStop) { rt_task_sleep(sleepTime); } if(gShouldStop) break; if(xenomai_gpio != 0) { // Set the test pin high xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN_MASK; } // Render from/to buffer 1 // Convert short (16-bit) samples to float for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++) audioInBuffer[n] = (float)pru_buffer_audio_adc[n + audio_buffer_frames * 2] / 32768.0; if(spi_enabled) render(spi_buffer_frames, audio_buffer_frames, audioInBuffer, audioOutBuffer, &pru_buffer_spi_adc[spi_buffer_frames * 8], &pru_buffer_spi_dac[spi_buffer_frames * 8]); else render(0, audio_buffer_frames, audioInBuffer, audioOutBuffer, 0, 0); // Convert float back to short for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++) { int out = audioOutBuffer[n] * 32768.0; if(out < -32768) out = -32768; else if(out > 32767) out = 32767; pru_buffer_audio_dac[n + audio_buffer_frames * 2] = (int16_t)out; } if(xenomai_gpio != 0) { // Set the test pin high xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN_MASK; } } // Tell PRU to stop pru_buffer_comm[PRU_SHOULD_STOP] = 1; free(audioInBuffer); free(audioOutBuffer); } // Wait for an interrupt from the PRU indicate it is finished void PRU::waitForFinish() { if(!running) return; prussdrv_pru_wait_event (PRU_EVTOUT_0); prussdrv_pru_clear_event(pru_number == 0 ? PRU0_ARM_INTERRUPT : PRU1_ARM_INTERRUPT); } // Turn off the PRU when done void PRU::disable() { /* Disable PRU and close memory mapping*/ prussdrv_pru_disable(pru_number); prussdrv_exit(); running = false; } // Debugging void PRU::setGPIOTestPin() { if(!xenomai_gpio) return; xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN2_MASK; } void PRU::clearGPIOTestPin() { if(!xenomai_gpio) return; xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN2_MASK; }