view 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
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/digital_gpio_mapping.h"
#include "../include/GPIOcontrol.h"
#include "../include/render.h"
#include "../include/pru_rtaudio_bin.h"

#include <iostream>
#include <stdlib.h>
#include <cstdio>
#include <cerrno>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.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_MEM_DIGITAL_OFFSET 0x1000 //Offset within PRU-SHARED RAM
#define MEM_DIGITAL_BUFFER1_OFFSET 0x400 //Start pointer to DIGITAL_BUFFER1, which is 256 words.
											// 256 is the maximum number of frames allowed
#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_SPI_NUM_CHANNELS 10
#define PRU_USE_DIGITAL    11

short int digitalPins[NUM_DIGITALS]={
		GPIO_NO_BIT_0,
		GPIO_NO_BIT_1,
		GPIO_NO_BIT_2,
		GPIO_NO_BIT_3,
		GPIO_NO_BIT_4,
		GPIO_NO_BIT_5,
		GPIO_NO_BIT_6,
		GPIO_NO_BIT_7,
		GPIO_NO_BIT_8,
		GPIO_NO_BIT_9,
		GPIO_NO_BIT_10,
		GPIO_NO_BIT_11,
		GPIO_NO_BIT_12,
		GPIO_NO_BIT_13,
		GPIO_NO_BIT_14,
		GPIO_NO_BIT_15,
};

#define PRU_SAMPLE_INTERVAL_NS 11338	// 88200Hz per SPI sample = 11.338us

#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), spi_num_channels(0), 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 use_digital, 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(use_digital){
		printf("gNumDigitalChannels: %d;\n",gNumDigitalChannels);
		for(int i=0; i<gNumDigitalChannels; i++){
			if(gpio_export(digitalPins[i])) {
				if(gRTAudioVerbose)
					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
			}
			if(gpio_set_dir(digitalPins[i], INPUT_PIN)) {
				if(gRTAudioVerbose)
					cerr << "Error: Couldn't set direction on digital GPIO pin " << digitalPins[i] << "\n";
				return -1;
			}
		}
		digital_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(digital_enabled){
		for(int i=0; i<gNumDigitalChannels; i++){
			gpio_unexport(digitalPins[i]);
		}
	}
	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, int spi_channels, 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;

	/* Set number of SPI ADC / DAC channels to use. This implicitly
	 * also determines the sample rate relative to the audio clock
	 * (half audio clock for 8 channels, full audio clock for 4,
	 * double audio clock for 2)
	 */
	spi_num_channels = spi_channels;

    /* 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_number == 0 ? PRU_EVTOUT_0 : PRU_EVTOUT_1)) {
    	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 * spi_num_channels / 4;
    digital_buffer_frames = audio_buffer_frames;

    /* 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)*bufsize samples later */
	pru_buffer_audio_adc = &pru_buffer_audio_dac[4 * audio_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 N(ch)*2(buffers)*bufsize samples */
		pru_buffer_spi_adc = &pru_buffer_spi_dac[2 * spi_num_channels * spi_buffer_frames];
	}
	else {
		pru_buffer_spi_dac = pru_buffer_spi_adc = 0;
	}

	if(digital_enabled) {
		prussdrv_map_prumem (PRUSS0_SHARED_DATARAM, (void **)&pruMem);
		pru_buffer_digital = (uint32_t *)&pruMem[PRU_MEM_DIGITAL_OFFSET/sizeof(uint32_t)];
	}
	else {
		pru_buffer_digital = 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;
    	pru_buffer_comm[PRU_SPI_NUM_CHANNELS] = spi_num_channels;
    }
    else {
    	pru_buffer_comm[PRU_USE_SPI] = 0;
    	pru_buffer_comm[PRU_SPI_NUM_CHANNELS] = 0;
    }
    if(digital_enabled) {
    	pru_buffer_comm[PRU_USE_DIGITAL] = 1;
//TODO: add mask
    }
    else {
    	pru_buffer_comm[PRU_USE_DIGITAL] = 0;

    }

    /* Clear ADC and DAC memory.*/
    //TODO: this initialisation should only address the memory effectively used by these buffers, i.e.:depend on the number of frames
    //  (otherwise might cause issues if we move memory locations later on)
    if(spi_enabled) {
		for(int i = 0; i < PRU_MEM_DAC_LENGTH / 2; i++)
			pru_buffer_spi_dac[i] = 0;
		if(digital_enabled){
			for(int i = 0; i < PRU_MEM_DIGITAL_OFFSET*2; i++)
				pru_buffer_digital[i] = 0x0000ffff; // set to all inputs, to avoid unexpected spikes
		}
    }
	for(int i = 0; i < PRU_MEM_MCASP_LENGTH / 2; i++)
		pru_buffer_audio_dac[i] = 0;
//TODO: maybe the lines below are to be deleted, as we removed the test code from pru_rtaudio.p ?
	/* 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 */
	if(pru_number == 0)
		prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT);
	else
		prussdrv_pru_clear_event(PRU_EVTOUT_1, PRU1_ARM_INTERRUPT);
	/* Load and execute binary on PRU */
	if(filename[0] == '\0') { //if the string is empty, load the embedded code
		if(gRTAudioVerbose)
			rt_printf("Using embedded PRU code\n");
		if(prussdrv_exec_code(pru_number, PRUcode, sizeof(PRUcode))) {
			rt_printf("Failed to execute PRU code\n");
			return 1;
		}
	} else {
		if(gRTAudioVerbose)
			rt_printf("Using PRU code from %s\n",filename);
		if(prussdrv_exec_program(pru_number, filename)) {
			rt_printf("Failed to execute PRU code from %s\n", filename);
			return 1;
		}
	}

    running = true;
    return 0;
}
uint32_t empty[1024]={0x0};

// 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_num_channels / 2) * spi_buffer_frames / 4;
	float *audioInBuffer, *audioOutBuffer;
	float *analogInBuffer, *analogOutBuffer, *lastAnalogOutFrame;
	uint32_t *digitalBuffer0, *digitalBuffer1, *lastDigitalBuffer;

	audioInBuffer = (float *)malloc(2 * audio_buffer_frames * sizeof(float));
	audioOutBuffer = (float *)malloc(2 * audio_buffer_frames * sizeof(float));
	analogInBuffer = (float *)malloc(spi_num_channels * spi_buffer_frames * sizeof(float));
	analogOutBuffer = (float *)malloc(spi_num_channels * spi_buffer_frames * sizeof(float));
	lastAnalogOutFrame = (float *)malloc(spi_num_channels * sizeof(float));
	digitalBuffer0 = pru_buffer_digital; 
	digitalBuffer1 = pru_buffer_digital+MEM_DIGITAL_BUFFER1_OFFSET/sizeof(uint32_t);
    digital_buffer_frames = digital_enabled ? audio_buffer_frames : 0; //TODO: find a more elegant solution for when the digital is disabled e.g.:
    																	// - embed in the digitalWrite/Read macros a check whether digital is enabled
    																	// - allocate some memory in ARM just to allow render() to run regardless.
    																	//     in this case it can be digitalBuffer0 == digitalBuffer1
	printf("digital_buffer_frames: %d;\n",digital_buffer_frames);
    lastDigitalBuffer = (uint32_t *)malloc(digital_buffer_frames*sizeof(uint32_t)); //temp buffer to hold previous states
	if(audioInBuffer == 0 || audioOutBuffer == 0) {
		rt_printf("Error: couldn't allocate audio buffers\n");
		return;
	}
	if(analogInBuffer == 0 || analogOutBuffer == 0 || lastAnalogOutFrame == 0) {
		rt_printf("Error: couldn't allocate analog buffers\n");
		return;
	}
	if(lastDigitalBuffer == 0) {
		rt_printf("Error: couldn't allocate digital buffers\n");
		return;
	}

	for(unsigned int n=0; n<digital_buffer_frames; n++){ //initialize lastDigitalFrames to all inputs
		lastDigitalBuffer[n]= 0x0000ffff;
	}
	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) {
			for(unsigned int n = 0; n < spi_num_channels * spi_buffer_frames; n++)
				analogInBuffer[n] = (float)pru_buffer_spi_adc[n] / 65536.0;
			//initialize the output buffer with the values that were in the last frame of the previous output
			for(int n = 0; n < spi_num_channels; n++){
				for(unsigned int j = 0; j < spi_buffer_frames; j++){
					analogOutBuffer[j*spi_buffer_frames + n] = lastAnalogOutFrame[n];
				}
			}
        //use past digital values to initialize the array properly.
		//For each frame:
        //- pins previously set as outputs will keep the output value they had in the last frame of the previous buffer,
        //- pins previously set as inputs will carry the newly read input value
            if(digital_enabled){
                for(unsigned int n = 0; n < digital_buffer_frames; n++){ 
                    uint16_t inputs=lastDigitalBuffer[n]&0xffff;//half-word, has 1 for inputs and 0 for outputs
//                    printf("inputs: 0x%x\n",inputs);
                    uint16_t outputs=~inputs; //half-word has 1 for outputs and 0 for inputs;
                    digitalBuffer0[n]=(lastDigitalBuffer[digital_buffer_frames-1]&(outputs<<16))| //keep output values set in the last frame of the previous buffer
                                         (digitalBuffer0[n]&(inputs<<16))   | //inputs from current digitalBuffer0[n];
                                         (lastDigitalBuffer[n]&(inputs));     //keep pin configuration from previous digitalBuffer1[n]
//                    digitalBuffer0[n]=digitalBufferTemp[n]; //ignores inputs
                }
            }
			render(spi_buffer_frames, digital_buffer_frames, audio_buffer_frames, audioInBuffer, audioOutBuffer,
					analogInBuffer, analogOutBuffer, digitalBuffer0);
			//remember the content of the lastAnalogOutFrame
			for(int n = 0; n < spi_num_channels; n++){
				lastAnalogOutFrame[n] = analogOutBuffer[spi_buffer_frames*(spi_buffer_frames-1) + n];
			}
			for(unsigned int n = 0; n < spi_num_channels * spi_buffer_frames; n++) {
				int out = analogOutBuffer[n] * 65536.0;
				if(out < 0) out = 0;
				else if(out > 65535) out = 65535;
				pru_buffer_spi_dac[n] = (uint16_t)out;
			}
            if(digital_enabled){ // keep track of past digital values
                for(unsigned int n = 0; n < digital_buffer_frames; n++){ 
                    lastDigitalBuffer[n]=digitalBuffer0[n];
                }
            }
		}
		else
			render(0, 0, audio_buffer_frames, audioInBuffer, audioOutBuffer, 0, 0, 0); // we still pass digitalBuffer, just it is unused
        // 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) {
			 //convert input values TODO: move to PRU
			for(unsigned int n = 0; n < spi_num_channels * spi_buffer_frames; n++){
				analogInBuffer[n] = (float)pru_buffer_spi_adc[n + spi_buffer_frames * spi_num_channels] / 65536.0;
			}
			//initialize the output buffer with the values that were in the last frame of the previous output
			for(int n = 0; n < spi_num_channels; n++){
				for(unsigned int j = 0; j < spi_buffer_frames; j++){
					analogOutBuffer[j*spi_buffer_frames + n] = lastAnalogOutFrame[n];
				}
			}
            if(digital_enabled){
                for(unsigned int n = 0; n < digital_buffer_frames; n++){
                    uint16_t inputs=lastDigitalBuffer[n]&0xffff;//half-word, has 1 for inputs and 0 for outputs
                    uint16_t outputs=~inputs; //half-word has 1 for outputs and one for inputs;
                    digitalBuffer1[n]=(lastDigitalBuffer[digital_buffer_frames-1]&(outputs<<16))| //keep output values set in the last frame of the  previous buffer
                                         (digitalBuffer1[n]&(inputs<<16))   | //inputs from current digitalBuffer1[n];
                                         (lastDigitalBuffer[n]&(inputs));     //keep pin configuration from previous digitalBuffer1[n]
//                    digitalBuffer1[n]=digitalBufferTemp[n]; //ignores inputs
                }
            }
			render(spi_buffer_frames, digital_buffer_frames, audio_buffer_frames, audioInBuffer, audioOutBuffer,
					analogInBuffer, analogOutBuffer, digitalBuffer1);
			//remember the content of the lastAnalogOutFrame
			for(int n = 0; n < spi_num_channels; n++){
				lastAnalogOutFrame[n] = analogOutBuffer[spi_buffer_frames*(spi_buffer_frames-1) + n];
			}

			for(unsigned int n = 0; n < spi_num_channels * spi_buffer_frames; n++) {
				int out = analogOutBuffer[n] * 65536.0;
				if(out < 0) out = 0;
				else if(out > 65535) out = 65535;
				pru_buffer_spi_dac[n + spi_buffer_frames * spi_num_channels] = (uint16_t)out;
			}
            if(digital_enabled){ // keep track of past digital values
                for(unsigned int n = 0; n < digital_buffer_frames; n++){ 
                    lastDigitalBuffer[n]=digitalBuffer1[n];
                }
            }
		}
		else
			render(0, 0, audio_buffer_frames, audioInBuffer, audioOutBuffer, 0, 0, 0); // we still pass digitalBuffer, just it is unused

		// 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(analogOutBuffer);
	free(audioInBuffer);
	free(audioOutBuffer);
	free(analogInBuffer);
	free(lastAnalogOutFrame);
    free(lastDigitalBuffer);
}

// Wait for an interrupt from the PRU indicate it is finished
void PRU::waitForFinish()
{
	if(!running)
		return;
    prussdrv_pru_wait_event (pru_number == 0 ? PRU_EVTOUT_0 : PRU_EVTOUT_1);
	if(pru_number == 0)
		prussdrv_pru_clear_event(PRU_EVTOUT_0, PRU0_ARM_INTERRUPT);
	else
		prussdrv_pru_clear_event(PRU_EVTOUT_1, 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;
}