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@0: #include "../include/render.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@0: #define PRU_MEM_MCASP_LENGTH 0x2000 // 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@0: #define PRU_LED_ADDRESS 6 andrewm@0: #define PRU_LED_PIN_MASK 7 andrewm@0: #define PRU_FRAME_COUNT 8 andrewm@0: #define PRU_USE_SPI 9 andrewm@12: #define PRU_SPI_NUM_CHANNELS 10 giuliomoro@38: #define PRU_USE_DIGITAL 11 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: andrewm@0: extern int gShouldStop; andrewm@0: extern int gRTAudioVerbose; andrewm@0: andrewm@0: // Constructor: specify a PRU number (0 or 1) andrewm@0: PRU::PRU() andrewm@0: : pru_number(0), running(false), spi_enabled(false), gpio_enabled(false), led_enabled(false), andrewm@12: gpio_test_pin_enabled(false), spi_num_channels(0), 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 giuliomoro@19: int PRU::prepareGPIO(int use_spi, int use_digital, int include_test_pin, int include_led) andrewm@0: { andrewm@0: if(use_spi) { 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@0: spi_enabled = true; andrewm@0: } andrewm@0: giuliomoro@19: if(use_digital){ giuliomoro@19: printf("gNumDigitalChannels: %d;\n",gNumDigitalChannels); giuliomoro@19: for(int i=0; i 65535) out = 65535; giuliomoro@16: pru_buffer_spi_dac[n] = (uint16_t)out; giuliomoro@16: } giuliomoro@19: if(digital_enabled){ // keep track of past digital values giuliomoro@19: for(unsigned int n = 0; n < digital_buffer_frames; n++){ giuliomoro@23: lastDigitalBuffer[n]=digitalBuffer0[n]; giuliomoro@16: } giuliomoro@16: } giuliomoro@16: } andrewm@0: else giuliomoro@19: render(0, 0, audio_buffer_frames, audioInBuffer, audioOutBuffer, 0, 0, 0); // we still pass digitalBuffer, just it is unused giuliomoro@16: // Convert float back to short andrewm@0: for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++) { andrewm@0: int out = audioOutBuffer[n] * 32768.0; andrewm@0: if(out < -32768) out = -32768; andrewm@0: else if(out > 32767) out = 32767; andrewm@0: pru_buffer_audio_dac[n] = (int16_t)out; andrewm@0: } andrewm@0: 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@0: // Wait for PRU to move to buffer 0 andrewm@0: while(pru_buffer_comm[PRU_CURRENT_BUFFER] != 0 && !gShouldStop) { andrewm@0: rt_task_sleep(sleepTime); andrewm@0: } andrewm@0: andrewm@0: if(gShouldStop) andrewm@0: break; andrewm@0: 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@0: // Render from/to buffer 1 andrewm@0: andrewm@0: // Convert short (16-bit) samples to float andrewm@0: for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++) andrewm@0: audioInBuffer[n] = (float)pru_buffer_audio_adc[n + audio_buffer_frames * 2] / 32768.0; andrewm@0: giuliomoro@16: if(spi_enabled) { giuliomoro@23: //convert input values TODO: move to PRU giuliomoro@23: for(unsigned int n = 0; n < spi_num_channels * spi_buffer_frames; n++){ giuliomoro@19: analogInBuffer[n] = (float)pru_buffer_spi_adc[n + spi_buffer_frames * spi_num_channels] / 65536.0; giuliomoro@23: } giuliomoro@23: //initialize the output buffer with the values that were in the last frame of the previous output giuliomoro@23: for(int n = 0; n < spi_num_channels; n++){ giuliomoro@23: for(unsigned int j = 0; j < spi_buffer_frames; j++){ giuliomoro@23: analogOutBuffer[j*spi_buffer_frames + n] = lastAnalogOutFrame[n]; giuliomoro@23: } giuliomoro@23: } giuliomoro@19: if(digital_enabled){ giuliomoro@23: for(unsigned int n = 0; n < digital_buffer_frames; n++){ giuliomoro@23: uint16_t inputs=lastDigitalBuffer[n]&0xffff;//half-word, has 1 for inputs and 0 for outputs giuliomoro@16: uint16_t outputs=~inputs; //half-word has 1 for outputs and one for inputs; giuliomoro@23: digitalBuffer1[n]=(lastDigitalBuffer[digital_buffer_frames-1]&(outputs<<16))| //keep output values set in the last frame of the previous buffer giuliomoro@19: (digitalBuffer1[n]&(inputs<<16)) | //inputs from current digitalBuffer1[n]; giuliomoro@23: (lastDigitalBuffer[n]&(inputs)); //keep pin configuration from previous digitalBuffer1[n] giuliomoro@19: // digitalBuffer1[n]=digitalBufferTemp[n]; //ignores inputs giuliomoro@16: } giuliomoro@16: } giuliomoro@19: render(spi_buffer_frames, digital_buffer_frames, audio_buffer_frames, audioInBuffer, audioOutBuffer, giuliomoro@19: analogInBuffer, analogOutBuffer, digitalBuffer1); giuliomoro@23: //remember the content of the lastAnalogOutFrame giuliomoro@23: for(int n = 0; n < spi_num_channels; n++){ giuliomoro@23: lastAnalogOutFrame[n] = analogOutBuffer[spi_buffer_frames*(spi_buffer_frames-1) + n]; giuliomoro@23: } giuliomoro@23: giuliomoro@16: for(unsigned int n = 0; n < spi_num_channels * spi_buffer_frames; n++) { giuliomoro@19: int out = analogOutBuffer[n] * 65536.0; giuliomoro@16: if(out < 0) out = 0; giuliomoro@16: else if(out > 65535) out = 65535; giuliomoro@16: pru_buffer_spi_dac[n + spi_buffer_frames * spi_num_channels] = (uint16_t)out; giuliomoro@16: } giuliomoro@19: if(digital_enabled){ // keep track of past digital values giuliomoro@19: for(unsigned int n = 0; n < digital_buffer_frames; n++){ giuliomoro@23: lastDigitalBuffer[n]=digitalBuffer1[n]; giuliomoro@16: } giuliomoro@16: } giuliomoro@16: } andrewm@0: else giuliomoro@19: render(0, 0, audio_buffer_frames, audioInBuffer, audioOutBuffer, 0, 0, 0); // we still pass digitalBuffer, just it is unused andrewm@0: andrewm@0: // Convert float back to short andrewm@0: for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++) { andrewm@0: int out = audioOutBuffer[n] * 32768.0; andrewm@0: if(out < -32768) out = -32768; andrewm@0: else if(out > 32767) out = 32767; andrewm@0: pru_buffer_audio_dac[n + audio_buffer_frames * 2] = (int16_t)out; andrewm@0: } andrewm@0: 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@0: andrewm@0: // Tell PRU to stop andrewm@0: pru_buffer_comm[PRU_SHOULD_STOP] = 1; andrewm@0: giuliomoro@19: free(analogOutBuffer); andrewm@0: free(audioInBuffer); andrewm@0: free(audioOutBuffer); giuliomoro@19: free(analogInBuffer); giuliomoro@23: free(lastAnalogOutFrame); giuliomoro@23: free(lastDigitalBuffer); 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@15: prussdrv_pru_wait_event (pru_number == 0 ? PRU_EVTOUT_0 : PRU_EVTOUT_1); andrewm@15: if(pru_number == 0) andrewm@15: prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT); andrewm@15: else andrewm@15: prussdrv_pru_clear_event(PRU_EVTOUT_1, PRU1_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: }