andrewm@0: /* andrewm@0: * PRU.cpp andrewm@0: * andrewm@0: * Code for communicating with the Programmable Realtime Unit (PRU) andrewm@0: * on the BeagleBone AM335x series processors. The PRU loads and runs andrewm@0: * a separate code image compiled from an assembly file. Here it is andrewm@0: * used to handle audio and SPI ADC/DAC data. andrewm@0: * andrewm@0: * This code is specific to the PRU code in the assembly file; for example, andrewm@0: * it uses certain GPIO resources that correspond to that image. andrewm@0: * andrewm@0: * Created on: May 27, 2014 andrewm@0: * Author: andrewm andrewm@0: */ andrewm@0: andrewm@0: #include "../include/PRU.h" andrewm@0: #include "../include/prussdrv.h" andrewm@0: #include "../include/pruss_intc_mapping.h" giuliomoro@19: #include "../include/digital_gpio_mapping.h" andrewm@0: #include "../include/GPIOcontrol.h" andrewm@45: #include "../include/BeagleRT.h" andrewm@15: #include "../include/pru_rtaudio_bin.h" andrewm@0: andrewm@0: #include andrewm@0: #include andrewm@0: #include andrewm@0: #include andrewm@0: #include andrewm@0: #include giuliomoro@16: #include andrewm@0: andrewm@0: // Xenomai-specific includes andrewm@0: #include andrewm@0: #include andrewm@0: #include andrewm@0: #include andrewm@0: andrewm@0: using namespace std; andrewm@0: andrewm@0: #define PRU_MEM_MCASP_OFFSET 0x2000 // Offset within PRU-SHARED RAM andrewm@253: #define PRU_MEM_MCASP_LENGTH 0x1000 // Length of McASP memory, in bytes andrewm@0: #define PRU_MEM_DAC_OFFSET 0x0 // Offset within PRU0 RAM andrewm@0: #define PRU_MEM_DAC_LENGTH 0x2000 // Length of ADC+DAC memory, in bytes andrewm@0: #define PRU_MEM_COMM_OFFSET 0x0 // Offset within PRU-SHARED RAM giuliomoro@19: #define PRU_MEM_DIGITAL_OFFSET 0x1000 //Offset within PRU-SHARED RAM giuliomoro@19: #define MEM_DIGITAL_BUFFER1_OFFSET 0x400 //Start pointer to DIGITAL_BUFFER1, which is 256 words. giuliomoro@16: // 256 is the maximum number of frames allowed andrewm@0: #define PRU_SHOULD_STOP 0 andrewm@0: #define PRU_CURRENT_BUFFER 1 andrewm@0: #define PRU_BUFFER_FRAMES 2 andrewm@0: #define PRU_SHOULD_SYNC 3 andrewm@0: #define PRU_SYNC_ADDRESS 4 andrewm@0: #define PRU_SYNC_PIN_MASK 5 andrewm@253: #define PRU_LED_ADDRESS 6 andrewm@253: #define PRU_LED_PIN_MASK 7 andrewm@253: #define PRU_FRAME_COUNT 8 andrewm@253: #define PRU_USE_SPI 9 andrewm@12: #define PRU_SPI_NUM_CHANNELS 10 andrewm@253: #define PRU_USE_DIGITAL 11 andrewm@253: #define PRU_PRU_NUMBER 12 giuliomoro@16: giuliomoro@19: short int digitalPins[NUM_DIGITALS]={ giuliomoro@16: GPIO_NO_BIT_0, giuliomoro@16: GPIO_NO_BIT_1, giuliomoro@16: GPIO_NO_BIT_2, giuliomoro@16: GPIO_NO_BIT_3, giuliomoro@16: GPIO_NO_BIT_4, giuliomoro@16: GPIO_NO_BIT_5, giuliomoro@16: GPIO_NO_BIT_6, giuliomoro@16: GPIO_NO_BIT_7, giuliomoro@16: GPIO_NO_BIT_8, giuliomoro@16: GPIO_NO_BIT_9, giuliomoro@16: GPIO_NO_BIT_10, giuliomoro@16: GPIO_NO_BIT_11, giuliomoro@16: GPIO_NO_BIT_12, giuliomoro@16: GPIO_NO_BIT_13, giuliomoro@16: GPIO_NO_BIT_14, giuliomoro@16: GPIO_NO_BIT_15, giuliomoro@16: }; andrewm@0: andrewm@12: #define PRU_SAMPLE_INTERVAL_NS 11338 // 88200Hz per SPI sample = 11.338us andrewm@0: andrewm@0: #define GPIO0_ADDRESS 0x44E07000 andrewm@0: #define GPIO1_ADDRESS 0x4804C000 andrewm@0: #define GPIO_SIZE 0x198 andrewm@0: #define GPIO_CLEARDATAOUT (0x190 / 4) andrewm@0: #define GPIO_SETDATAOUT (0x194 / 4) andrewm@0: andrewm@0: #define TEST_PIN_GPIO_BASE GPIO0_ADDRESS // Use GPIO0(31) for debugging andrewm@0: #define TEST_PIN_MASK (1 << 31) andrewm@0: #define TEST_PIN2_MASK (1 << 26) andrewm@0: andrewm@0: #define USERLED3_GPIO_BASE GPIO1_ADDRESS // GPIO1(24) is user LED 3 andrewm@0: #define USERLED3_PIN_MASK (1 << 24) andrewm@0: andrewm@0: const unsigned int PRU::kPruGPIODACSyncPin = 5; // GPIO0(5); P9-17 andrewm@0: const unsigned int PRU::kPruGPIOADCSyncPin = 48; // GPIO1(16); P9-15 andrewm@0: andrewm@0: const unsigned int PRU::kPruGPIOTestPin = 60; // GPIO1(28); P9-12 andrewm@0: const unsigned int PRU::kPruGPIOTestPin2 = 31; // GPIO0(31); P9-13 andrewm@0: const unsigned int PRU::kPruGPIOTestPin3 = 26; // GPIO0(26); P8-14 andrewm@0: giuliomoro@231: extern int gShouldStop; andrewm@0: extern int gRTAudioVerbose; andrewm@0: andrewm@0: // Constructor: specify a PRU number (0 or 1) andrewm@45: PRU::PRU(BeagleRTContext *input_context) andrewm@45: : context(input_context), pru_number(0), running(false), analog_enabled(false), andrewm@45: digital_enabled(false), gpio_enabled(false), led_enabled(false), andrewm@45: gpio_test_pin_enabled(false), andrewm@45: pru_buffer_comm(0), pru_buffer_spi_dac(0), pru_buffer_spi_adc(0), andrewm@45: pru_buffer_digital(0), pru_buffer_audio_dac(0), pru_buffer_audio_adc(0), andrewm@45: xenomai_gpio_fd(-1), xenomai_gpio(0) andrewm@0: { andrewm@0: andrewm@0: } andrewm@0: andrewm@0: // Destructor andrewm@0: PRU::~PRU() andrewm@0: { andrewm@0: if(running) andrewm@0: disable(); andrewm@0: if(gpio_enabled) andrewm@0: cleanupGPIO(); andrewm@0: if(xenomai_gpio_fd >= 0) andrewm@0: close(xenomai_gpio_fd); andrewm@0: } andrewm@0: andrewm@0: // Prepare the GPIO pins needed for the PRU andrewm@0: // If include_test_pin is set, the GPIO output andrewm@0: // is also prepared for an output which can be andrewm@0: // viewed on a scope. If include_led is set, andrewm@0: // user LED 3 on the BBB is taken over by the PRU andrewm@0: // to indicate activity andrewm@45: int PRU::prepareGPIO(int include_test_pin, int include_led) andrewm@0: { andrewm@45: if(context->analogFrames != 0) { andrewm@0: // Prepare DAC CS/ pin: output, high to begin andrewm@0: if(gpio_export(kPruGPIODACSyncPin)) { andrewm@0: if(gRTAudioVerbose) andrewm@0: cout << "Warning: couldn't export DAC sync pin\n"; andrewm@0: } andrewm@0: if(gpio_set_dir(kPruGPIODACSyncPin, OUTPUT_PIN)) { andrewm@0: if(gRTAudioVerbose) andrewm@0: cout << "Couldn't set direction on DAC sync pin\n"; andrewm@0: return -1; andrewm@0: } andrewm@0: if(gpio_set_value(kPruGPIODACSyncPin, HIGH)) { andrewm@0: if(gRTAudioVerbose) andrewm@0: cout << "Couldn't set value on DAC sync pin\n"; andrewm@0: return -1; andrewm@0: } andrewm@0: andrewm@0: // Prepare ADC CS/ pin: output, high to begin andrewm@0: if(gpio_export(kPruGPIOADCSyncPin)) { andrewm@0: if(gRTAudioVerbose) andrewm@0: cout << "Warning: couldn't export ADC sync pin\n"; andrewm@0: } andrewm@0: if(gpio_set_dir(kPruGPIOADCSyncPin, OUTPUT_PIN)) { andrewm@0: if(gRTAudioVerbose) andrewm@0: cout << "Couldn't set direction on ADC sync pin\n"; andrewm@0: return -1; andrewm@0: } andrewm@0: if(gpio_set_value(kPruGPIOADCSyncPin, HIGH)) { andrewm@0: if(gRTAudioVerbose) andrewm@0: cout << "Couldn't set value on ADC sync pin\n"; andrewm@0: return -1; andrewm@0: } andrewm@0: andrewm@45: analog_enabled = true; andrewm@0: } andrewm@0: andrewm@45: if(context->digitalFrames != 0){ andrewm@45: for(unsigned int i = 0; i < context->digitalChannels; i++){ giuliomoro@19: if(gpio_export(digitalPins[i])) { giuliomoro@16: if(gRTAudioVerbose) giuliomoro@38: 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: } giuliomoro@38: if(gpio_set_dir(digitalPins[i], INPUT_PIN)) { giuliomoro@16: if(gRTAudioVerbose) giuliomoro@38: cerr << "Error: Couldn't set direction on digital GPIO pin " << digitalPins[i] << "\n"; giuliomoro@16: return -1; giuliomoro@16: } giuliomoro@16: } andrewm@45: digital_enabled = true; giuliomoro@16: } giuliomoro@16: andrewm@0: if(include_test_pin) { andrewm@0: // Prepare GPIO test output (for debugging), low to begin andrewm@0: if(gpio_export(kPruGPIOTestPin)) { andrewm@0: if(gRTAudioVerbose) andrewm@0: cout << "Warning: couldn't export GPIO test pin\n"; andrewm@0: } andrewm@0: if(gpio_set_dir(kPruGPIOTestPin, OUTPUT_PIN)) { andrewm@0: if(gRTAudioVerbose) andrewm@0: cout << "Couldn't set direction on GPIO test pin\n"; andrewm@0: return -1; andrewm@0: } andrewm@0: if(gpio_set_value(kPruGPIOTestPin, LOW)) { andrewm@0: if(gRTAudioVerbose) andrewm@0: cout << "Couldn't set value on GPIO test pin\n"; andrewm@0: return -1; andrewm@0: } andrewm@0: andrewm@0: if(gpio_export(kPruGPIOTestPin2)) { andrewm@0: if(gRTAudioVerbose) andrewm@0: cout << "Warning: couldn't export GPIO test pin 2\n"; andrewm@0: } andrewm@0: if(gpio_set_dir(kPruGPIOTestPin2, OUTPUT_PIN)) { andrewm@0: if(gRTAudioVerbose) andrewm@0: cout << "Couldn't set direction on GPIO test pin 2\n"; andrewm@0: return -1; andrewm@0: } andrewm@0: if(gpio_set_value(kPruGPIOTestPin2, LOW)) { andrewm@0: if(gRTAudioVerbose) andrewm@0: cout << "Couldn't set value on GPIO test pin 2\n"; andrewm@0: return -1; andrewm@0: } andrewm@0: andrewm@0: if(gpio_export(kPruGPIOTestPin3)) { andrewm@0: if(gRTAudioVerbose) andrewm@0: cout << "Warning: couldn't export GPIO test pin 3\n"; andrewm@0: } andrewm@0: if(gpio_set_dir(kPruGPIOTestPin3, OUTPUT_PIN)) { andrewm@0: if(gRTAudioVerbose) andrewm@0: cout << "Couldn't set direction on GPIO test pin 3\n"; andrewm@0: return -1; andrewm@0: } andrewm@0: if(gpio_set_value(kPruGPIOTestPin3, LOW)) { andrewm@0: if(gRTAudioVerbose) andrewm@0: cout << "Couldn't set value on GPIO test pin 3\n"; andrewm@0: return -1; andrewm@0: } andrewm@0: gpio_test_pin_enabled = true; andrewm@0: } andrewm@0: andrewm@0: if(include_led) { andrewm@0: // Turn off system function for LED3 so it can be reused by PRU andrewm@0: led_set_trigger(3, "none"); andrewm@0: led_enabled = true; andrewm@0: } andrewm@0: andrewm@0: gpio_enabled = true; andrewm@0: andrewm@0: return 0; andrewm@0: } andrewm@0: andrewm@0: // Clean up the GPIO at the end andrewm@0: void PRU::cleanupGPIO() andrewm@0: { andrewm@0: if(!gpio_enabled) andrewm@0: return; andrewm@45: if(analog_enabled) { andrewm@0: gpio_unexport(kPruGPIODACSyncPin); andrewm@0: gpio_unexport(kPruGPIOADCSyncPin); andrewm@0: } giuliomoro@19: if(digital_enabled){ andrewm@45: for(unsigned int i = 0; i < context->digitalChannels; i++){ giuliomoro@19: gpio_unexport(digitalPins[i]); giuliomoro@16: } giuliomoro@16: } andrewm@0: if(gpio_test_pin_enabled) { andrewm@0: gpio_unexport(kPruGPIOTestPin); andrewm@0: gpio_unexport(kPruGPIOTestPin2); andrewm@0: gpio_unexport(kPruGPIOTestPin3); andrewm@0: } andrewm@0: if(led_enabled) { andrewm@0: // Set LED back to default eMMC status andrewm@0: // TODO: make it go back to its actual value before this program, andrewm@0: // rather than the system default andrewm@0: led_set_trigger(3, "mmc1"); andrewm@0: } andrewm@0: gpio_enabled = gpio_test_pin_enabled = false; andrewm@0: } andrewm@0: andrewm@0: // Initialise and open the PRU andrewm@12: int PRU::initialise(int pru_num, int frames_per_buffer, int spi_channels, bool xenomai_test_pin) andrewm@0: { andrewm@0: uint32_t *pruMem = 0; andrewm@0: andrewm@0: if(!gpio_enabled) { andrewm@0: rt_printf("initialise() called before GPIO enabled\n"); andrewm@0: return 1; andrewm@0: } andrewm@0: andrewm@0: pru_number = pru_num; andrewm@0: andrewm@0: /* Initialize structure used by prussdrv_pruintc_intc */ andrewm@0: /* PRUSS_INTC_INITDATA is found in pruss_intc_mapping.h */ andrewm@0: tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA; andrewm@0: andrewm@0: /* Allocate and initialize memory */ andrewm@0: prussdrv_init(); andrewm@45: if(prussdrv_open(PRU_EVTOUT_0)) { andrewm@0: rt_printf("Failed to open PRU driver\n"); andrewm@0: return 1; andrewm@0: } andrewm@0: andrewm@0: /* Map PRU's INTC */ andrewm@0: prussdrv_pruintc_init(&pruss_intc_initdata); andrewm@0: andrewm@0: /* Map PRU memory to pointers */ andrewm@0: prussdrv_map_prumem (PRUSS0_SHARED_DATARAM, (void **)&pruMem); andrewm@0: pru_buffer_comm = (uint32_t *)&pruMem[PRU_MEM_COMM_OFFSET/sizeof(uint32_t)]; andrewm@0: pru_buffer_audio_dac = (int16_t *)&pruMem[PRU_MEM_MCASP_OFFSET/sizeof(uint32_t)]; andrewm@0: andrewm@12: /* ADC memory starts 2(ch)*2(buffers)*bufsize samples later */ andrewm@45: pru_buffer_audio_adc = &pru_buffer_audio_dac[4 * context->audioFrames]; andrewm@0: andrewm@45: if(analog_enabled) { andrewm@0: prussdrv_map_prumem (pru_number == 0 ? PRUSS0_PRU0_DATARAM : PRUSS0_PRU1_DATARAM, (void **)&pruMem); andrewm@0: pru_buffer_spi_dac = (uint16_t *)&pruMem[PRU_MEM_DAC_OFFSET/sizeof(uint32_t)]; andrewm@0: andrewm@12: /* ADC memory starts after N(ch)*2(buffers)*bufsize samples */ andrewm@45: pru_buffer_spi_adc = &pru_buffer_spi_dac[2 * context->analogChannels * context->analogFrames]; andrewm@0: } andrewm@0: else { andrewm@0: pru_buffer_spi_dac = pru_buffer_spi_adc = 0; andrewm@0: } andrewm@0: giuliomoro@19: if(digital_enabled) { giuliomoro@16: prussdrv_map_prumem (PRUSS0_SHARED_DATARAM, (void **)&pruMem); giuliomoro@19: pru_buffer_digital = (uint32_t *)&pruMem[PRU_MEM_DIGITAL_OFFSET/sizeof(uint32_t)]; giuliomoro@16: } giuliomoro@16: else { giuliomoro@19: pru_buffer_digital = 0; giuliomoro@16: } andrewm@45: andrewm@0: /* Set up flags */ andrewm@0: pru_buffer_comm[PRU_SHOULD_STOP] = 0; andrewm@0: pru_buffer_comm[PRU_CURRENT_BUFFER] = 0; andrewm@45: pru_buffer_comm[PRU_BUFFER_FRAMES] = context->analogFrames; andrewm@0: pru_buffer_comm[PRU_SHOULD_SYNC] = 0; andrewm@0: pru_buffer_comm[PRU_SYNC_ADDRESS] = 0; andrewm@0: pru_buffer_comm[PRU_SYNC_PIN_MASK] = 0; andrewm@253: pru_buffer_comm[PRU_PRU_NUMBER] = pru_number; andrewm@0: if(led_enabled) { andrewm@0: pru_buffer_comm[PRU_LED_ADDRESS] = USERLED3_GPIO_BASE; andrewm@0: pru_buffer_comm[PRU_LED_PIN_MASK] = USERLED3_PIN_MASK; andrewm@0: } andrewm@0: else { andrewm@0: pru_buffer_comm[PRU_LED_ADDRESS] = 0; andrewm@0: pru_buffer_comm[PRU_LED_PIN_MASK] = 0; andrewm@0: } andrewm@45: if(analog_enabled) { andrewm@0: pru_buffer_comm[PRU_USE_SPI] = 1; andrewm@45: pru_buffer_comm[PRU_SPI_NUM_CHANNELS] = context->analogChannels; andrewm@0: } andrewm@0: else { andrewm@0: pru_buffer_comm[PRU_USE_SPI] = 0; andrewm@12: pru_buffer_comm[PRU_SPI_NUM_CHANNELS] = 0; andrewm@0: } giuliomoro@19: if(digital_enabled) { giuliomoro@38: pru_buffer_comm[PRU_USE_DIGITAL] = 1; giuliomoro@38: //TODO: add mask giuliomoro@16: } giuliomoro@16: else { giuliomoro@38: pru_buffer_comm[PRU_USE_DIGITAL] = 0; giuliomoro@38: giuliomoro@16: } andrewm@0: giuliomoro@38: /* Clear ADC and DAC memory.*/ giuliomoro@38: //TODO: this initialisation should only address the memory effectively used by these buffers, i.e.:depend on the number of frames giuliomoro@38: // (otherwise might cause issues if we move memory locations later on) andrewm@45: if(analog_enabled) { andrewm@0: for(int i = 0; i < PRU_MEM_DAC_LENGTH / 2; i++) andrewm@0: pru_buffer_spi_dac[i] = 0; giuliomoro@38: if(digital_enabled){ giuliomoro@38: for(int i = 0; i < PRU_MEM_DIGITAL_OFFSET*2; i++) giuliomoro@38: pru_buffer_digital[i] = 0x0000ffff; // set to all inputs, to avoid unexpected spikes giuliomoro@38: } andrewm@0: } andrewm@0: for(int i = 0; i < PRU_MEM_MCASP_LENGTH / 2; i++) andrewm@0: pru_buffer_audio_dac[i] = 0; andrewm@45: andrewm@0: /* If using GPIO test pin for Xenomai (for debugging), initialise the pointer now */ andrewm@0: if(xenomai_test_pin && xenomai_gpio_fd < 0) { andrewm@0: xenomai_gpio_fd = open("/dev/mem", O_RDWR); andrewm@0: if(xenomai_gpio_fd < 0) andrewm@0: rt_printf("Unable to open /dev/mem for GPIO test pin\n"); andrewm@0: else { andrewm@0: xenomai_gpio = (uint32_t *)mmap(0, GPIO_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, xenomai_gpio_fd, TEST_PIN_GPIO_BASE); andrewm@0: if(xenomai_gpio == MAP_FAILED) { andrewm@0: rt_printf("Unable to map GPIO address for test pin\n"); andrewm@0: xenomai_gpio = 0; andrewm@0: close(xenomai_gpio_fd); andrewm@0: xenomai_gpio_fd = -1; andrewm@0: } andrewm@0: } andrewm@0: } andrewm@0: andrewm@81: // Allocate audio buffers andrewm@81: context->audioIn = (float *)malloc(2 * context->audioFrames * sizeof(float)); andrewm@81: context->audioOut = (float *)malloc(2 * context->audioFrames * sizeof(float)); andrewm@81: if(context->audioIn == 0 || context->audioOut == 0) { andrewm@81: rt_printf("Error: couldn't allocate audio buffers\n"); andrewm@81: return 1; andrewm@81: } andrewm@81: andrewm@81: // Allocate analog buffers andrewm@81: if(analog_enabled) { andrewm@81: context->analogIn = (float *)malloc(context->analogChannels * context->analogFrames * sizeof(float)); andrewm@81: context->analogOut = (float *)malloc(context->analogChannels * context->analogFrames * sizeof(float)); andrewm@81: last_analog_out_frame = (float *)malloc(context->analogChannels * sizeof(float)); andrewm@81: andrewm@81: if(context->analogIn == 0 || context->analogOut == 0 || last_analog_out_frame == 0) { andrewm@81: rt_printf("Error: couldn't allocate analog buffers\n"); andrewm@81: return 1; andrewm@81: } andrewm@81: andrewm@81: memset(last_analog_out_frame, 0, context->analogChannels * sizeof(float)); andrewm@81: } andrewm@81: andrewm@81: // Allocate digital buffers andrewm@81: digital_buffer0 = pru_buffer_digital; andrewm@81: digital_buffer1 = pru_buffer_digital + MEM_DIGITAL_BUFFER1_OFFSET / sizeof(uint32_t); andrewm@81: if(digital_enabled) { andrewm@81: last_digital_buffer = (uint32_t *)malloc(context->digitalFrames * sizeof(uint32_t)); //temp buffer to hold previous states andrewm@81: if(last_digital_buffer == 0) { andrewm@81: rt_printf("Error: couldn't allocate digital buffers\n"); andrewm@81: return 1; andrewm@81: } andrewm@81: andrewm@81: for(unsigned int n = 0; n < context->digitalFrames; n++){ andrewm@81: // Initialize lastDigitalFrames to all inputs andrewm@81: last_digital_buffer[n] = 0x0000ffff; andrewm@81: } andrewm@81: } andrewm@81: andrewm@81: context->digital = digital_buffer0; andrewm@81: andrewm@0: return 0; andrewm@0: } andrewm@0: andrewm@0: // Run the code image in the specified file giuliomoro@16: int PRU::start(char * const filename) andrewm@0: { andrewm@0: /* Clear any old interrupt */ andrewm@45: prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT); andrewm@45: giuliomoro@16: /* Load and execute binary on PRU */ giuliomoro@16: if(filename[0] == '\0') { //if the string is empty, load the embedded code giuliomoro@16: if(gRTAudioVerbose) giuliomoro@16: rt_printf("Using embedded PRU code\n"); giuliomoro@16: if(prussdrv_exec_code(pru_number, PRUcode, sizeof(PRUcode))) { giuliomoro@16: rt_printf("Failed to execute PRU code\n"); giuliomoro@16: return 1; giuliomoro@16: } giuliomoro@16: } else { giuliomoro@16: if(gRTAudioVerbose) giuliomoro@16: rt_printf("Using PRU code from %s\n",filename); giuliomoro@16: if(prussdrv_exec_program(pru_number, filename)) { giuliomoro@16: rt_printf("Failed to execute PRU code from %s\n", filename); giuliomoro@16: return 1; giuliomoro@16: } giuliomoro@16: } andrewm@0: andrewm@0: running = true; andrewm@0: return 0; andrewm@0: } andrewm@0: andrewm@0: // Main loop to read and write data from/to PRU andrewm@45: void PRU::loop(RT_INTR *pru_interrupt, void *userData) andrewm@0: { andrewm@50: #ifdef BEAGLERT_USE_XENOMAI_INTERRUPTS andrewm@50: RTIME irqTimeout = PRU_SAMPLE_INTERVAL_NS * 1024; // Timeout for PRU interrupt: about 10ms, much longer than any expected period andrewm@50: #else andrewm@0: // Polling interval is 1/4 of the period andrewm@50: RTIME sleepTime = PRU_SAMPLE_INTERVAL_NS * (context->analogChannels / 2) * context->analogFrames / 4; andrewm@50: #endif andrewm@45: andrewm@45: uint32_t pru_audio_offset, pru_spi_offset; andrewm@0: andrewm@81: // Before starting, look at the last state of the analog and digital outputs which might andrewm@81: // have been changed by the user during the setup() function. This lets us start with pin andrewm@81: // directions and output values at something other than defaults. andrewm@81: andrewm@81: if(analog_enabled) { andrewm@81: if(context->flags & BEAGLERT_FLAG_ANALOG_OUTPUTS_PERSIST) { andrewm@81: // Remember the content of the last_analog_out_frame andrewm@81: for(unsigned int ch = 0; ch < context->analogChannels; ch++){ andrewm@81: last_analog_out_frame[ch] = context->analogOut[context->analogChannels * (context->analogFrames - 1) + ch]; andrewm@81: } andrewm@81: } andrewm@0: } andrewm@45: andrewm@45: if(digital_enabled) { andrewm@45: for(unsigned int n = 0; n < context->digitalFrames; n++){ andrewm@81: last_digital_buffer[n] = context->digital[n]; andrewm@45: } giuliomoro@38: } andrewm@45: andrewm@45: // TESTING andrewm@50: // uint32_t testCount = 0; andrewm@45: // RTIME startTime = rt_timer_read(); andrewm@45: andrewm@56: #ifdef BEAGLERT_USE_XENOMAI_INTERRUPTS andrewm@56: int result; andrewm@56: #else andrewm@50: // Which buffer the PRU was last processing andrewm@50: uint32_t lastPRUBuffer = 0; andrewm@50: #endif andrewm@50: andrewm@0: while(!gShouldStop) { andrewm@50: #ifdef BEAGLERT_USE_XENOMAI_INTERRUPTS andrewm@45: // Wait for PRU to move to change buffers; andrewm@45: // PRU will send an interrupts which we wait for andrewm@45: rt_intr_enable(pru_interrupt); andrewm@45: while(!gShouldStop) { andrewm@45: result = rt_intr_wait(pru_interrupt, irqTimeout); andrewm@45: if(result >= 0) andrewm@45: break; andrewm@45: else if(result == -ETIMEDOUT) andrewm@45: rt_printf("Warning: PRU timeout!\n"); andrewm@45: else { andrewm@45: rt_printf("Error: wait for interrupt failed (%d)\n", result); andrewm@45: gShouldStop = 1; andrewm@45: } andrewm@0: } andrewm@45: andrewm@45: // Clear pending PRU interrupt andrewm@45: prussdrv_pru_clear_event(PRU_EVTOUT_1, PRU1_ARM_INTERRUPT); andrewm@50: #else andrewm@50: // Poll andrewm@50: while(pru_buffer_comm[PRU_CURRENT_BUFFER] == lastPRUBuffer && !gShouldStop) { andrewm@50: rt_task_sleep(sleepTime); andrewm@50: } andrewm@50: andrewm@50: lastPRUBuffer = pru_buffer_comm[PRU_CURRENT_BUFFER]; andrewm@50: #endif andrewm@45: andrewm@0: if(gShouldStop) andrewm@0: break; andrewm@0: andrewm@45: // Check which buffer we're on-- will have been set right andrewm@45: // before the interrupt was asserted andrewm@45: if(pru_buffer_comm[PRU_CURRENT_BUFFER] == 1) { andrewm@45: // PRU is on buffer 1. We read and write to buffer 0 andrewm@45: pru_audio_offset = 0; andrewm@45: pru_spi_offset = 0; andrewm@45: if(digital_enabled) andrewm@81: context->digital = digital_buffer0; andrewm@45: } andrewm@45: else { andrewm@45: // PRU is on buffer 0. We read and write to buffer 1 andrewm@45: pru_audio_offset = context->audioFrames * 2; andrewm@45: pru_spi_offset = context->analogFrames * context->analogChannels; andrewm@45: if(digital_enabled) andrewm@81: context->digital = digital_buffer1; andrewm@45: } andrewm@45: andrewm@45: // FIXME: some sort of margin is needed here to prevent the audio andrewm@45: // code from completely eating the Linux system andrewm@50: // testCount++; andrewm@45: //rt_task_sleep(sleepTime*4); andrewm@45: //rt_task_sleep(sleepTime/4); andrewm@45: andrewm@0: if(xenomai_gpio != 0) { andrewm@0: // Set the test pin high andrewm@0: xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN_MASK; andrewm@0: } andrewm@0: andrewm@45: // Convert short (16-bit) samples to float andrewm@45: // TODO: NEON andrewm@45: for(unsigned int n = 0; n < 2 * context->audioFrames; n++) giuliomoro@231: context->audioIn[n] = (float)pru_buffer_audio_adc[n + pru_audio_offset] / 32768.0f; andrewm@0: andrewm@45: if(analog_enabled) { andrewm@45: // TODO: NEON andrewm@45: for(unsigned int n = 0; n < context->analogChannels * context->analogFrames; n++) giuliomoro@231: context->analogIn[n] = (float)pru_buffer_spi_adc[n + pru_spi_offset] / 65536.0f; andrewm@45: andrewm@45: if(context->flags & BEAGLERT_FLAG_ANALOG_OUTPUTS_PERSIST) { andrewm@45: // Initialize the output buffer with the values that were in the last frame of the previous output andrewm@45: for(unsigned int ch = 0; ch < context->analogChannels; ch++){ andrewm@45: for(unsigned int n = 0; n < context->analogFrames; n++){ andrewm@81: context->analogOut[n * context->analogChannels + ch] = last_analog_out_frame[ch]; andrewm@45: } giuliomoro@23: } giuliomoro@23: } andrewm@45: else { andrewm@45: // Outputs are 0 unless set otherwise andrewm@45: memset(context->analogOut, 0, context->analogChannels * context->analogFrames * sizeof(float)); giuliomoro@23: } andrewm@45: } andrewm@45: andrewm@45: if(digital_enabled){ andrewm@45: // Use past digital values to initialize the array properly. andrewm@45: // For each frame: andrewm@45: // - pins previously set as outputs will keep the output value they had in the last frame of the previous buffer, andrewm@45: // - pins previously set as inputs will carry the newly read input value andrewm@45: andrewm@45: for(unsigned int n = 0; n < context->digitalFrames; n++){ andrewm@81: uint16_t inputs = last_digital_buffer[n] & 0xffff; // half-word, has 1 for inputs and 0 for outputs andrewm@45: andrewm@45: uint16_t outputs = ~inputs; // half-word has 1 for outputs and 0 for inputs; andrewm@81: context->digital[n] = (last_digital_buffer[context->digitalFrames - 1] & (outputs << 16)) | // keep output values set in the last frame of the previous buffer andrewm@45: (context->digital[n] & (inputs << 16)) | // inputs from current context->digital[n]; andrewm@81: (last_digital_buffer[n] & (inputs)); // keep pin configuration from previous context->digital[n] andrewm@45: // context->digital[n]=digitalBufferTemp[n]; //ignores inputs andrewm@45: } andrewm@45: } andrewm@45: andrewm@45: // Call user render function andrewm@45: // *********************** andrewm@45: render(context, userData); andrewm@45: // *********************** andrewm@45: andrewm@45: if(analog_enabled) { andrewm@45: if(context->flags & BEAGLERT_FLAG_ANALOG_OUTPUTS_PERSIST) { andrewm@81: // Remember the content of the last_analog_out_frame andrewm@45: for(unsigned int ch = 0; ch < context->analogChannels; ch++){ andrewm@81: last_analog_out_frame[ch] = context->analogOut[context->analogChannels * (context->analogFrames - 1) + ch]; andrewm@45: } andrewm@45: } andrewm@45: andrewm@45: // Convert float back to short for SPI output andrewm@45: for(unsigned int n = 0; n < context->analogChannels * context->analogFrames; n++) { giuliomoro@231: int out = context->analogOut[n] * 65536.0f; giuliomoro@16: if(out < 0) out = 0; giuliomoro@16: else if(out > 65535) out = 65535; andrewm@45: pru_buffer_spi_dac[n + pru_spi_offset] = (uint16_t)out; giuliomoro@16: } giuliomoro@16: } andrewm@45: andrewm@45: if(digital_enabled) { // keep track of past digital values andrewm@45: for(unsigned int n = 0; n < context->digitalFrames; n++){ andrewm@81: last_digital_buffer[n] = context->digital[n]; andrewm@45: } andrewm@45: } andrewm@45: andrewm@45: // Convert float back to short for audio andrewm@45: // TODO: NEON andrewm@45: for(unsigned int n = 0; n < 2 * context->audioFrames; n++) { giuliomoro@231: int out = context->audioOut[n] * 32768.0f; andrewm@0: if(out < -32768) out = -32768; andrewm@0: else if(out > 32767) out = 32767; andrewm@45: pru_buffer_audio_dac[n + pru_audio_offset] = (int16_t)out; andrewm@0: } andrewm@0: andrewm@52: // Increment total number of samples that have elapsed andrewm@52: context->audioSampleCount += context->audioFrames; andrewm@52: andrewm@0: if(xenomai_gpio != 0) { andrewm@0: // Set the test pin high andrewm@0: xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN_MASK; andrewm@0: } andrewm@0: andrewm@45: // FIXME: TESTING!! andrewm@50: // if(testCount > 100000) andrewm@50: // break; andrewm@45: } andrewm@0: andrewm@50: #ifdef BEAGLERT_USE_XENOMAI_INTERRUPTS andrewm@45: // Turn off the interrupt for the PRU if it isn't already off andrewm@45: rt_intr_disable(pru_interrupt); andrewm@50: #endif andrewm@0: andrewm@45: // FIXME: TESTING andrewm@45: // RTIME endTime = rt_timer_read(); andrewm@45: // RTIME diffTime = endTime - startTime; andrewm@45: // 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: andrewm@0: // Tell PRU to stop andrewm@0: pru_buffer_comm[PRU_SHOULD_STOP] = 1; andrewm@0: andrewm@45: // Wait two buffer lengths for the PRU to finish andrewm@45: rt_task_sleep(PRU_SAMPLE_INTERVAL_NS * context->analogFrames * 4 * 2); andrewm@45: andrewm@45: // Clean up after ourselves andrewm@45: free(context->audioIn); andrewm@45: free(context->audioOut); andrewm@45: andrewm@45: if(analog_enabled) { andrewm@45: free(context->analogIn); andrewm@45: free(context->analogOut); andrewm@81: free(last_analog_out_frame); andrewm@45: } andrewm@45: andrewm@45: if(digital_enabled) { andrewm@81: free(last_digital_buffer); andrewm@45: } andrewm@45: andrewm@45: context->audioIn = context->audioOut = 0; andrewm@45: context->analogIn = context->analogOut = 0; andrewm@45: context->digital = 0; andrewm@0: } andrewm@0: andrewm@0: // Wait for an interrupt from the PRU indicate it is finished andrewm@0: void PRU::waitForFinish() andrewm@0: { andrewm@0: if(!running) andrewm@0: return; andrewm@45: prussdrv_pru_wait_event (PRU_EVTOUT_0); andrewm@45: prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT); andrewm@0: } andrewm@0: andrewm@0: // Turn off the PRU when done andrewm@0: void PRU::disable() andrewm@0: { andrewm@0: /* Disable PRU and close memory mapping*/ andrewm@0: prussdrv_pru_disable(pru_number); andrewm@0: prussdrv_exit(); andrewm@0: running = false; andrewm@0: } andrewm@0: andrewm@0: // Debugging andrewm@0: void PRU::setGPIOTestPin() andrewm@0: { andrewm@0: if(!xenomai_gpio) andrewm@0: return; andrewm@0: xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN2_MASK; andrewm@0: } andrewm@0: andrewm@0: void PRU::clearGPIOTestPin() andrewm@0: { andrewm@0: if(!xenomai_gpio) andrewm@0: return; andrewm@0: xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN2_MASK; andrewm@0: }