diff core/PRU.cpp @ 16:670be80463a3 matrix_gpio

- analog matrixIn/matrixOut are now mapped as floats from 0 to 1 - use of an external PRU code can be enabled with -P <filename> - 16 channels of programmable GPIO can be accessed straight from render() either writing directly to the matrixGpio[] array or using digitalWrite(), digitalRead(), setDigitalDirection() macros from Utilities.h .
author Giulio Moro <giuliomoro@yahoo.it>
date Mon, 27 Apr 2015 13:01:57 +0100
parents 901d205d1a3c
children c98863e63174
line wrap: on
line diff
--- a/core/PRU.cpp	Sat Feb 07 16:41:56 2015 +0000
+++ b/core/PRU.cpp	Mon Apr 27 13:01:57 2015 +0100
@@ -16,6 +16,7 @@
 #include "../include/PRU.h"
 #include "../include/prussdrv.h"
 #include "../include/pruss_intc_mapping.h"
+#include "../include/matrix_gpio_mapping.h"
 #include "../include/GPIOcontrol.h"
 #include "../include/render.h"
 #include "../include/pru_rtaudio_bin.h"
@@ -26,6 +27,7 @@
 #include <cerrno>
 #include <fcntl.h>
 #include <sys/mman.h>
+#include <unistd.h>
 
 // Xenomai-specific includes
 #include <sys/mman.h>
@@ -40,7 +42,9 @@
 #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_MATRIX_GPIO_OFFSET 0x1000 //Offset within PRU-SHARED RAM
+#define MEM_MATRIX_GPIO_BUFFER1_OFFSET 0x400 //Start pointer to MATRIX_GPIO_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
@@ -52,6 +56,26 @@
 #define PRU_FRAME_COUNT		8
 #define PRU_USE_SPI			9
 #define PRU_SPI_NUM_CHANNELS 10
+#define PRU_USE_GPIO_MATRIX 11
+
+short int matrixGpioPins[NUM_MATRIX_GPIOS]={
+		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
 
@@ -103,7 +127,7 @@
 // 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)
+int PRU::prepareGPIO(int use_spi, int use_matrix_gpio, int include_test_pin, int include_led)
 {
 	if(use_spi) {
 		// Prepare DAC CS/ pin: output, high to begin
@@ -141,6 +165,27 @@
 		spi_enabled = true;
 	}
 
+	if(use_matrix_gpio){
+		printf("gNumMatrixGpioChannels: %d;\n",gNumMatrixGpioChannels);
+		for(int i=0; i<gNumMatrixGpioChannels; i++){
+			if(gpio_export(matrixGpioPins[i])) {
+				if(gRTAudioVerbose)
+					cout << "Warning: couldn't export matrix GPIO pin " << matrixGpioPins[i] << "\n";
+			}
+			if(gpio_set_dir(matrixGpioPins[i], OUTPUT_PIN)) {
+				if(gRTAudioVerbose)
+					cout << "Couldn't set direction on matrix GPIO pin " << matrixGpioPins[i] << "\n";
+				return -1;
+			}
+			if(gpio_set_value(matrixGpioPins[i], HIGH)) {
+				if(gRTAudioVerbose)
+					cout << "Couldn't set value on matrix GPIO pin " << matrixGpioPins[i] << "\n";
+				return -1;
+			}
+		}
+		matrix_gpio_enabled=true;
+	}
+
 	if(include_test_pin) {
 		// Prepare GPIO test output (for debugging), low to begin
 		if(gpio_export(kPruGPIOTestPin)) {
@@ -210,6 +255,11 @@
 		gpio_unexport(kPruGPIODACSyncPin);
 		gpio_unexport(kPruGPIOADCSyncPin);
 	}
+	if(matrix_gpio_enabled){
+		for(int i=0; i<gNumMatrixGpioChannels; i++){
+			gpio_unexport(matrixGpioPins[i]);
+		}
+	}
 	if(gpio_test_pin_enabled) {
 		gpio_unexport(kPruGPIOTestPin);
 		gpio_unexport(kPruGPIOTestPin2);
@@ -221,7 +271,6 @@
 		// rather than the system default
 		led_set_trigger(3, "mmc1");
 	}
-
 	gpio_enabled = gpio_test_pin_enabled = false;
 }
 
@@ -260,6 +309,7 @@
 
     spi_buffer_frames = frames_per_buffer;
     audio_buffer_frames = spi_buffer_frames * spi_num_channels / 4;
+    matrix_gpio_buffer_frames = audio_buffer_frames;
 
     /* Map PRU memory to pointers */
 	prussdrv_map_prumem (PRUSS0_SHARED_DATARAM, (void **)&pruMem);
@@ -280,6 +330,13 @@
 		pru_buffer_spi_dac = pru_buffer_spi_adc = 0;
 	}
 
+	if(matrix_gpio_enabled) {
+		prussdrv_map_prumem (PRUSS0_SHARED_DATARAM, (void **)&pruMem);
+		pru_buffer_matrix_gpio = (uint32_t *)&pruMem[PRU_MEM_MATRIX_GPIO_OFFSET/sizeof(uint32_t)];
+	}
+	else {
+		pru_buffer_matrix_gpio = 0;
+	}
     /* Set up flags */
     pru_buffer_comm[PRU_SHOULD_STOP] = 0;
     pru_buffer_comm[PRU_CURRENT_BUFFER] = 0;
@@ -303,6 +360,14 @@
     	pru_buffer_comm[PRU_USE_SPI] = 0;
     	pru_buffer_comm[PRU_SPI_NUM_CHANNELS] = 0;
     }
+    if(matrix_gpio_enabled) {
+    	pru_buffer_comm[PRU_USE_GPIO_MATRIX] = 1;
+    	pru_buffer_comm[NUM_MATRIX_GPIOS] = spi_num_channels;
+    }
+    else {
+    	pru_buffer_comm[PRU_USE_GPIO_MATRIX] = 0;
+    	pru_buffer_comm[NUM_MATRIX_GPIOS] = 0;
+    }
 
     /* Clear ADC and DAC memory */
     if(spi_enabled) {
@@ -311,7 +376,7 @@
     }
 	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);
@@ -332,23 +397,34 @@
 }
 
 // Run the code image in the specified file
-int PRU::start()
+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(prussdrv_exec_code(pru_number, PRUcode, sizeof(PRUcode))) {
-    	rt_printf("Failed to execute PRU code\n");
-    	return 1;
-    }
+	/* 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()
@@ -356,15 +432,34 @@
 	// 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 *matrixInBuffer, *matrixOutBuffer;
+	uint32_t *matrixGpioBuffer0, *matrixGpioBuffer1, *matrixGpioBufferTemp;
 
 	audioInBuffer = (float *)malloc(2 * audio_buffer_frames * sizeof(float));
 	audioOutBuffer = (float *)malloc(2 * audio_buffer_frames * sizeof(float));
-
+	matrixInBuffer = (float *)malloc(spi_num_channels * spi_buffer_frames * sizeof(float));
+	matrixOutBuffer = (float *)malloc(spi_num_channels * spi_buffer_frames * sizeof(float));
+	matrixGpioBuffer0 = pru_buffer_matrix_gpio; 
+	matrixGpioBuffer1 = pru_buffer_matrix_gpio+MEM_MATRIX_GPIO_BUFFER1_OFFSET/sizeof(uint32_t);
+   matrix_gpio_buffer_frames = matrix_gpio_enabled ? audio_buffer_frames : 0; //TODO: find a more elegant solution for when the matrix_gpio is disabled e.g.:
+    																	// - embed in the digitalWrite/Read macros a check whether matrix_gpio is enabled
+    																	// - allocate some memory in ARM just to allow render() to run regardless.
+    																	//     in this case it can be matrixGpioBuffer0 == matrixGpioBuffer1
+	printf("matrix_gpio_buffer_frames: %d;\n",matrix_gpio_buffer_frames);
+    matrixGpioBufferTemp = (uint32_t *)malloc(matrix_gpio_buffer_frames*sizeof(uint32_t)); //temp buffer to hold previous states
 	if(audioInBuffer == 0 || audioOutBuffer == 0) {
-		rt_printf("Error: couldn't allocated audio buffers\n");
+		rt_printf("Error: couldn't allocate audio buffers\n");
 		return;
 	}
-
+	if(matrixInBuffer == 0 || matrixOutBuffer == 0) {
+		rt_printf("Error: couldn't allocate matrix buffers\n");
+		return;
+	}
+	if(matrixGpioBufferTemp == 0) {
+		rt_printf("Error: couldn't allocate matrix GPIO buffers\n");
+		return;
+	} 
+	
 	while(!gShouldStop) {
 		// Wait for PRU to move to buffer 1
 		while(pru_buffer_comm[PRU_CURRENT_BUFFER] == 0 && !gShouldStop) {
@@ -383,14 +478,40 @@
 		// 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);
+		if(spi_enabled) {
+			for(unsigned int n = 0; n < spi_num_channels * spi_buffer_frames; n++)
+				matrixInBuffer[n] = (float)pru_buffer_spi_adc[n] / 65536.0;
+        //use past matrix_gpio values to initialize the array properly:
+        //- pins previously set as outputs will keep their previously set output value,
+        //- pins previously set as inputs will carry the newly read input value
+            if(matrix_gpio_enabled){
+                for(unsigned int n = 0; n < matrix_gpio_buffer_frames; n++){ 
+                    uint16_t inputs=matrixGpioBufferTemp[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;
+                    matrixGpioBuffer0[n]=(matrixGpioBufferTemp[n]&(outputs<<16))| //keep output values set in previous matrixGpioBuffer1[n]
+                                         (matrixGpioBuffer0[n]&(inputs<<16))   | //inputs from current matrixGpioBuffer0[n];
+                                         (matrixGpioBufferTemp[n]&(inputs));     //keep pin configuration from previous matrixGpioBuffer1[n]
+//                    matrixGpioBuffer0[n]=matrixGpioBufferTemp[n]; //ignores inputs
+                }
+            }
+			render(spi_buffer_frames, matrix_gpio_buffer_frames, audio_buffer_frames, audioInBuffer, audioOutBuffer,
+					matrixInBuffer, matrixOutBuffer, matrixGpioBuffer0);
+			for(unsigned int n = 0; n < spi_num_channels * spi_buffer_frames; n++) {
+				int out = matrixOutBuffer[n] * 65536.0;
+				if(out < 0) out = 0;
+				else if(out > 65535) out = 65535;
+				pru_buffer_spi_dac[n] = (uint16_t)out;
+			}
+            if(matrix_gpio_enabled){ // keep track of past matrix_gpio values
+                for(unsigned int n = 0; n < matrix_gpio_buffer_frames; n++){ 
+                    matrixGpioBufferTemp[n]=matrixGpioBuffer0[n];
+                }
+            }
+		}
 		else
-			render(0, audio_buffer_frames, audioInBuffer, audioOutBuffer, 0, 0);
-
-		// Convert float back to short
+			render(0, 0, audio_buffer_frames, audioInBuffer, audioOutBuffer, 0, 0, 0); // we still pass matrixGpioBuffer, 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;
@@ -422,11 +543,39 @@
 		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 * spi_num_channels], &pru_buffer_spi_dac[spi_buffer_frames * spi_num_channels]);
+		if(spi_enabled) {
+			for(unsigned int n = 0; n < spi_num_channels * spi_buffer_frames; n++)
+				matrixInBuffer[n] = (float)pru_buffer_spi_adc[n + spi_buffer_frames * spi_num_channels] / 65536.0;
+
+            //use past matrix_gpio values to initialize the array properly:
+            //- pins previously set as outputs will keep their previously set output value,
+            //- pins previously set as inputs will carry the newly read input value
+            if(matrix_gpio_enabled){
+                for(unsigned int n = 0; n < matrix_gpio_buffer_frames; n++){ 
+                    uint16_t inputs=matrixGpioBufferTemp[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;
+                    matrixGpioBuffer1[n]=(matrixGpioBufferTemp[n]&(outputs<<16))| //keep output values set in previous matrixGpioBuffer1[n]
+                                         (matrixGpioBuffer1[n]&(inputs<<16))   | //inputs from current matrixGpioBuffer1[n];
+                                         (matrixGpioBufferTemp[n]&(inputs));     //keep pin configuration from previous matrixGpioBuffer1[n]
+//                    matrixGpioBuffer1[n]=matrixGpioBufferTemp[n]; //ignores inputs
+                }
+            }
+			render(spi_buffer_frames, matrix_gpio_buffer_frames, audio_buffer_frames, audioInBuffer, audioOutBuffer,
+					matrixInBuffer, matrixOutBuffer, matrixGpioBuffer1);
+			for(unsigned int n = 0; n < spi_num_channels * spi_buffer_frames; n++) {
+				int out = matrixOutBuffer[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(matrix_gpio_enabled){ // keep track of past matrix_gpio values
+                for(unsigned int n = 0; n < matrix_gpio_buffer_frames; n++){ 
+                    matrixGpioBufferTemp[n]=matrixGpioBuffer1[n];
+                }
+            }
+		}
 		else
-			render(0, audio_buffer_frames, audioInBuffer, audioOutBuffer, 0, 0);
+			render(0, 0, audio_buffer_frames, audioInBuffer, audioOutBuffer, 0, 0, 0); // we still pass matrixGpioBuffer, just it is unused
 
 		// Convert float back to short
 		for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++) {
@@ -445,8 +594,11 @@
 	// Tell PRU to stop
 	pru_buffer_comm[PRU_SHOULD_STOP] = 1;
 
+	free(matrixOutBuffer);
 	free(audioInBuffer);
 	free(audioOutBuffer);
+	free(matrixInBuffer);
+    free(matrixGpioBufferTemp);
 }
 
 // Wait for an interrupt from the PRU indicate it is finished