| andrewm@0 | 1 /* | 
| andrewm@0 | 2  * PRU.cpp | 
| andrewm@0 | 3  * | 
| andrewm@0 | 4  * Code for communicating with the Programmable Realtime Unit (PRU) | 
| andrewm@0 | 5  * on the BeagleBone AM335x series processors. The PRU loads and runs | 
| andrewm@0 | 6  * a separate code image compiled from an assembly file. Here it is | 
| andrewm@0 | 7  * used to handle audio and SPI ADC/DAC data. | 
| andrewm@0 | 8  * | 
| andrewm@0 | 9  * This code is specific to the PRU code in the assembly file; for example, | 
| andrewm@0 | 10  * it uses certain GPIO resources that correspond to that image. | 
| andrewm@0 | 11  * | 
| andrewm@0 | 12  *  Created on: May 27, 2014 | 
| andrewm@0 | 13  *      Author: andrewm | 
| andrewm@0 | 14  */ | 
| andrewm@0 | 15 | 
| andrewm@0 | 16 #include "../include/PRU.h" | 
| andrewm@0 | 17 #include "../include/prussdrv.h" | 
| andrewm@0 | 18 #include "../include/pruss_intc_mapping.h" | 
| andrewm@0 | 19 #include "../include/GPIOcontrol.h" | 
| andrewm@0 | 20 #include "../include/render.h" | 
| andrewm@0 | 21 | 
| andrewm@0 | 22 #include <iostream> | 
| andrewm@0 | 23 #include <stdlib.h> | 
| andrewm@0 | 24 #include <cstdio> | 
| andrewm@0 | 25 #include <cerrno> | 
| andrewm@0 | 26 #include <fcntl.h> | 
| andrewm@0 | 27 #include <sys/mman.h> | 
| andrewm@0 | 28 | 
| andrewm@0 | 29 // Xenomai-specific includes | 
| andrewm@0 | 30 #include <sys/mman.h> | 
| andrewm@0 | 31 #include <native/task.h> | 
| andrewm@0 | 32 #include <native/timer.h> | 
| andrewm@0 | 33 #include <rtdk.h> | 
| andrewm@0 | 34 | 
| andrewm@0 | 35 using namespace std; | 
| andrewm@0 | 36 | 
| andrewm@0 | 37 #define PRU_MEM_MCASP_OFFSET 0x2000  // Offset within PRU-SHARED RAM | 
| andrewm@0 | 38 #define PRU_MEM_MCASP_LENGTH 0x2000  // Length of McASP memory, in bytes | 
| andrewm@0 | 39 #define PRU_MEM_DAC_OFFSET 0x0     // Offset within PRU0 RAM | 
| andrewm@0 | 40 #define PRU_MEM_DAC_LENGTH 0x2000  // Length of ADC+DAC memory, in bytes | 
| andrewm@0 | 41 #define PRU_MEM_COMM_OFFSET 0x0    // Offset within PRU-SHARED RAM | 
| andrewm@0 | 42 | 
| andrewm@0 | 43 #define PRU_SHOULD_STOP 	0 | 
| andrewm@0 | 44 #define PRU_CURRENT_BUFFER  1 | 
| andrewm@0 | 45 #define PRU_BUFFER_FRAMES   2 | 
| andrewm@0 | 46 #define PRU_SHOULD_SYNC     3 | 
| andrewm@0 | 47 #define PRU_SYNC_ADDRESS    4 | 
| andrewm@0 | 48 #define PRU_SYNC_PIN_MASK   5 | 
| andrewm@0 | 49 #define PRU_LED_ADDRESS		6 | 
| andrewm@0 | 50 #define PRU_LED_PIN_MASK	7 | 
| andrewm@0 | 51 #define PRU_FRAME_COUNT		8 | 
| andrewm@0 | 52 #define PRU_USE_SPI			9 | 
| andrewm@12 | 53 #define PRU_SPI_NUM_CHANNELS 10 | 
| andrewm@0 | 54 | 
| andrewm@12 | 55 #define PRU_SAMPLE_INTERVAL_NS 11338	// 88200Hz per SPI sample = 11.338us | 
| andrewm@0 | 56 | 
| andrewm@0 | 57 #define GPIO0_ADDRESS 		0x44E07000 | 
| andrewm@0 | 58 #define GPIO1_ADDRESS 		0x4804C000 | 
| andrewm@0 | 59 #define GPIO_SIZE			0x198 | 
| andrewm@0 | 60 #define GPIO_CLEARDATAOUT 	(0x190 / 4) | 
| andrewm@0 | 61 #define GPIO_SETDATAOUT 	(0x194 / 4) | 
| andrewm@0 | 62 | 
| andrewm@0 | 63 #define TEST_PIN_GPIO_BASE	GPIO0_ADDRESS	// Use GPIO0(31) for debugging | 
| andrewm@0 | 64 #define TEST_PIN_MASK		(1 << 31) | 
| andrewm@0 | 65 #define TEST_PIN2_MASK		(1 << 26) | 
| andrewm@0 | 66 | 
| andrewm@0 | 67 #define USERLED3_GPIO_BASE  GPIO1_ADDRESS // GPIO1(24) is user LED 3 | 
| andrewm@0 | 68 #define USERLED3_PIN_MASK   (1 << 24) | 
| andrewm@0 | 69 | 
| andrewm@0 | 70 const unsigned int PRU::kPruGPIODACSyncPin = 5;	// GPIO0(5); P9-17 | 
| andrewm@0 | 71 const unsigned int PRU::kPruGPIOADCSyncPin = 48; // GPIO1(16); P9-15 | 
| andrewm@0 | 72 | 
| andrewm@0 | 73 const unsigned int PRU::kPruGPIOTestPin = 60;	// GPIO1(28); P9-12 | 
| andrewm@0 | 74 const unsigned int PRU::kPruGPIOTestPin2 = 31;	// GPIO0(31); P9-13 | 
| andrewm@0 | 75 const unsigned int PRU::kPruGPIOTestPin3 = 26;	// GPIO0(26); P8-14 | 
| andrewm@0 | 76 | 
| andrewm@0 | 77 extern int gShouldStop; | 
| andrewm@0 | 78 extern int gRTAudioVerbose; | 
| andrewm@0 | 79 | 
| andrewm@0 | 80 // Constructor: specify a PRU number (0 or 1) | 
| andrewm@0 | 81 PRU::PRU() | 
| andrewm@0 | 82 : pru_number(0), running(false), spi_enabled(false), gpio_enabled(false), led_enabled(false), | 
| andrewm@12 | 83   gpio_test_pin_enabled(false), spi_num_channels(0), xenomai_gpio_fd(-1), xenomai_gpio(0) | 
| andrewm@0 | 84 { | 
| andrewm@0 | 85 | 
| andrewm@0 | 86 } | 
| andrewm@0 | 87 | 
| andrewm@0 | 88 // Destructor | 
| andrewm@0 | 89 PRU::~PRU() | 
| andrewm@0 | 90 { | 
| andrewm@0 | 91 	if(running) | 
| andrewm@0 | 92 		disable(); | 
| andrewm@0 | 93 	if(gpio_enabled) | 
| andrewm@0 | 94 		cleanupGPIO(); | 
| andrewm@0 | 95 	if(xenomai_gpio_fd >= 0) | 
| andrewm@0 | 96 		close(xenomai_gpio_fd); | 
| andrewm@0 | 97 } | 
| andrewm@0 | 98 | 
| andrewm@0 | 99 // Prepare the GPIO pins needed for the PRU | 
| andrewm@0 | 100 // If include_test_pin is set, the GPIO output | 
| andrewm@0 | 101 // is also prepared for an output which can be | 
| andrewm@0 | 102 // viewed on a scope. If include_led is set, | 
| andrewm@0 | 103 // user LED 3 on the BBB is taken over by the PRU | 
| andrewm@0 | 104 // to indicate activity | 
| andrewm@0 | 105 int PRU::prepareGPIO(int use_spi, int include_test_pin, int include_led) | 
| andrewm@0 | 106 { | 
| andrewm@0 | 107 	if(use_spi) { | 
| andrewm@0 | 108 		// Prepare DAC CS/ pin: output, high to begin | 
| andrewm@0 | 109 		if(gpio_export(kPruGPIODACSyncPin)) { | 
| andrewm@0 | 110 			if(gRTAudioVerbose) | 
| andrewm@0 | 111 				cout << "Warning: couldn't export DAC sync pin\n"; | 
| andrewm@0 | 112 		} | 
| andrewm@0 | 113 		if(gpio_set_dir(kPruGPIODACSyncPin, OUTPUT_PIN)) { | 
| andrewm@0 | 114 			if(gRTAudioVerbose) | 
| andrewm@0 | 115 				cout << "Couldn't set direction on DAC sync pin\n"; | 
| andrewm@0 | 116 			return -1; | 
| andrewm@0 | 117 		} | 
| andrewm@0 | 118 		if(gpio_set_value(kPruGPIODACSyncPin, HIGH)) { | 
| andrewm@0 | 119 			if(gRTAudioVerbose) | 
| andrewm@0 | 120 				cout << "Couldn't set value on DAC sync pin\n"; | 
| andrewm@0 | 121 			return -1; | 
| andrewm@0 | 122 		} | 
| andrewm@0 | 123 | 
| andrewm@0 | 124 		// Prepare ADC CS/ pin: output, high to begin | 
| andrewm@0 | 125 		if(gpio_export(kPruGPIOADCSyncPin)) { | 
| andrewm@0 | 126 			if(gRTAudioVerbose) | 
| andrewm@0 | 127 				cout << "Warning: couldn't export ADC sync pin\n"; | 
| andrewm@0 | 128 		} | 
| andrewm@0 | 129 		if(gpio_set_dir(kPruGPIOADCSyncPin, OUTPUT_PIN)) { | 
| andrewm@0 | 130 			if(gRTAudioVerbose) | 
| andrewm@0 | 131 				cout << "Couldn't set direction on ADC sync pin\n"; | 
| andrewm@0 | 132 			return -1; | 
| andrewm@0 | 133 		} | 
| andrewm@0 | 134 		if(gpio_set_value(kPruGPIOADCSyncPin, HIGH)) { | 
| andrewm@0 | 135 			if(gRTAudioVerbose) | 
| andrewm@0 | 136 				cout << "Couldn't set value on ADC sync pin\n"; | 
| andrewm@0 | 137 			return -1; | 
| andrewm@0 | 138 		} | 
| andrewm@0 | 139 | 
| andrewm@0 | 140 		spi_enabled = true; | 
| andrewm@0 | 141 	} | 
| andrewm@0 | 142 | 
| andrewm@0 | 143 	if(include_test_pin) { | 
| andrewm@0 | 144 		// Prepare GPIO test output (for debugging), low to begin | 
| andrewm@0 | 145 		if(gpio_export(kPruGPIOTestPin)) { | 
| andrewm@0 | 146 			if(gRTAudioVerbose) | 
| andrewm@0 | 147 				cout << "Warning: couldn't export GPIO test pin\n"; | 
| andrewm@0 | 148 		} | 
| andrewm@0 | 149 		if(gpio_set_dir(kPruGPIOTestPin, OUTPUT_PIN)) { | 
| andrewm@0 | 150 			if(gRTAudioVerbose) | 
| andrewm@0 | 151 				cout << "Couldn't set direction on GPIO test pin\n"; | 
| andrewm@0 | 152 			return -1; | 
| andrewm@0 | 153 		} | 
| andrewm@0 | 154 		if(gpio_set_value(kPruGPIOTestPin, LOW)) { | 
| andrewm@0 | 155 			if(gRTAudioVerbose) | 
| andrewm@0 | 156 				cout << "Couldn't set value on GPIO test pin\n"; | 
| andrewm@0 | 157 			return -1; | 
| andrewm@0 | 158 		} | 
| andrewm@0 | 159 | 
| andrewm@0 | 160 		if(gpio_export(kPruGPIOTestPin2)) { | 
| andrewm@0 | 161 			if(gRTAudioVerbose) | 
| andrewm@0 | 162 				cout << "Warning: couldn't export GPIO test pin 2\n"; | 
| andrewm@0 | 163 		} | 
| andrewm@0 | 164 		if(gpio_set_dir(kPruGPIOTestPin2, OUTPUT_PIN)) { | 
| andrewm@0 | 165 			if(gRTAudioVerbose) | 
| andrewm@0 | 166 				cout << "Couldn't set direction on GPIO test pin 2\n"; | 
| andrewm@0 | 167 			return -1; | 
| andrewm@0 | 168 		} | 
| andrewm@0 | 169 		if(gpio_set_value(kPruGPIOTestPin2, LOW)) { | 
| andrewm@0 | 170 			if(gRTAudioVerbose) | 
| andrewm@0 | 171 				cout << "Couldn't set value on GPIO test pin 2\n"; | 
| andrewm@0 | 172 			return -1; | 
| andrewm@0 | 173 		} | 
| andrewm@0 | 174 | 
| andrewm@0 | 175 		if(gpio_export(kPruGPIOTestPin3)) { | 
| andrewm@0 | 176 			if(gRTAudioVerbose) | 
| andrewm@0 | 177 				cout << "Warning: couldn't export GPIO test pin 3\n"; | 
| andrewm@0 | 178 		} | 
| andrewm@0 | 179 		if(gpio_set_dir(kPruGPIOTestPin3, OUTPUT_PIN)) { | 
| andrewm@0 | 180 			if(gRTAudioVerbose) | 
| andrewm@0 | 181 				cout << "Couldn't set direction on GPIO test pin 3\n"; | 
| andrewm@0 | 182 			return -1; | 
| andrewm@0 | 183 		} | 
| andrewm@0 | 184 		if(gpio_set_value(kPruGPIOTestPin3, LOW)) { | 
| andrewm@0 | 185 			if(gRTAudioVerbose) | 
| andrewm@0 | 186 				cout << "Couldn't set value on GPIO test pin 3\n"; | 
| andrewm@0 | 187 			return -1; | 
| andrewm@0 | 188 		} | 
| andrewm@0 | 189 		gpio_test_pin_enabled = true; | 
| andrewm@0 | 190 	} | 
| andrewm@0 | 191 | 
| andrewm@0 | 192 	if(include_led) { | 
| andrewm@0 | 193 		// Turn off system function for LED3 so it can be reused by PRU | 
| andrewm@0 | 194 		led_set_trigger(3, "none"); | 
| andrewm@0 | 195 		led_enabled = true; | 
| andrewm@0 | 196 	} | 
| andrewm@0 | 197 | 
| andrewm@0 | 198 	gpio_enabled = true; | 
| andrewm@0 | 199 | 
| andrewm@0 | 200 	return 0; | 
| andrewm@0 | 201 } | 
| andrewm@0 | 202 | 
| andrewm@0 | 203 // Clean up the GPIO at the end | 
| andrewm@0 | 204 void PRU::cleanupGPIO() | 
| andrewm@0 | 205 { | 
| andrewm@0 | 206 	if(!gpio_enabled) | 
| andrewm@0 | 207 		return; | 
| andrewm@0 | 208 	if(spi_enabled) { | 
| andrewm@0 | 209 		gpio_unexport(kPruGPIODACSyncPin); | 
| andrewm@0 | 210 		gpio_unexport(kPruGPIOADCSyncPin); | 
| andrewm@0 | 211 	} | 
| andrewm@0 | 212 	if(gpio_test_pin_enabled) { | 
| andrewm@0 | 213 		gpio_unexport(kPruGPIOTestPin); | 
| andrewm@0 | 214 		gpio_unexport(kPruGPIOTestPin2); | 
| andrewm@0 | 215 		gpio_unexport(kPruGPIOTestPin3); | 
| andrewm@0 | 216 	} | 
| andrewm@0 | 217 	if(led_enabled) { | 
| andrewm@0 | 218 		// Set LED back to default eMMC status | 
| andrewm@0 | 219 		// TODO: make it go back to its actual value before this program, | 
| andrewm@0 | 220 		// rather than the system default | 
| andrewm@0 | 221 		led_set_trigger(3, "mmc1"); | 
| andrewm@0 | 222 	} | 
| andrewm@0 | 223 | 
| andrewm@0 | 224 	gpio_enabled = gpio_test_pin_enabled = false; | 
| andrewm@0 | 225 } | 
| andrewm@0 | 226 | 
| andrewm@0 | 227 // Initialise and open the PRU | 
| andrewm@12 | 228 int PRU::initialise(int pru_num, int frames_per_buffer, int spi_channels, bool xenomai_test_pin) | 
| andrewm@0 | 229 { | 
| andrewm@0 | 230 	uint32_t *pruMem = 0; | 
| andrewm@0 | 231 | 
| andrewm@0 | 232 	if(!gpio_enabled) { | 
| andrewm@0 | 233 		rt_printf("initialise() called before GPIO enabled\n"); | 
| andrewm@0 | 234 		return 1; | 
| andrewm@0 | 235 	} | 
| andrewm@0 | 236 | 
| andrewm@0 | 237 	pru_number = pru_num; | 
| andrewm@0 | 238 | 
| andrewm@12 | 239 	/* Set number of SPI ADC / DAC channels to use. This implicitly | 
| andrewm@12 | 240 	 * also determines the sample rate relative to the audio clock | 
| andrewm@12 | 241 	 * (half audio clock for 8 channels, full audio clock for 4, | 
| andrewm@12 | 242 	 * double audio clock for 2) | 
| andrewm@12 | 243 	 */ | 
| andrewm@12 | 244 	spi_num_channels = spi_channels; | 
| andrewm@12 | 245 | 
| andrewm@0 | 246     /* Initialize structure used by prussdrv_pruintc_intc   */ | 
| andrewm@0 | 247     /* PRUSS_INTC_INITDATA is found in pruss_intc_mapping.h */ | 
| andrewm@0 | 248     tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA; | 
| andrewm@0 | 249 | 
| andrewm@0 | 250     /* Allocate and initialize memory */ | 
| andrewm@0 | 251     prussdrv_init(); | 
| andrewm@0 | 252     if(prussdrv_open(PRU_EVTOUT_0)) { | 
| andrewm@0 | 253     	rt_printf("Failed to open PRU driver\n"); | 
| andrewm@0 | 254     	return 1; | 
| andrewm@0 | 255     } | 
| andrewm@0 | 256 | 
| andrewm@0 | 257     /* Map PRU's INTC */ | 
| andrewm@0 | 258     prussdrv_pruintc_init(&pruss_intc_initdata); | 
| andrewm@0 | 259 | 
| andrewm@0 | 260     spi_buffer_frames = frames_per_buffer; | 
| andrewm@12 | 261     audio_buffer_frames = spi_buffer_frames * spi_num_channels / 4; | 
| andrewm@0 | 262 | 
| andrewm@0 | 263     /* Map PRU memory to pointers */ | 
| andrewm@0 | 264 	prussdrv_map_prumem (PRUSS0_SHARED_DATARAM, (void **)&pruMem); | 
| andrewm@0 | 265     pru_buffer_comm = (uint32_t *)&pruMem[PRU_MEM_COMM_OFFSET/sizeof(uint32_t)]; | 
| andrewm@0 | 266 	pru_buffer_audio_dac = (int16_t *)&pruMem[PRU_MEM_MCASP_OFFSET/sizeof(uint32_t)]; | 
| andrewm@0 | 267 | 
| andrewm@12 | 268 	/* ADC memory starts 2(ch)*2(buffers)*bufsize samples later */ | 
| andrewm@12 | 269 	pru_buffer_audio_adc = &pru_buffer_audio_dac[4 * audio_buffer_frames]; | 
| andrewm@0 | 270 | 
| andrewm@0 | 271 	if(spi_enabled) { | 
| andrewm@0 | 272 		prussdrv_map_prumem (pru_number == 0 ? PRUSS0_PRU0_DATARAM : PRUSS0_PRU1_DATARAM, (void **)&pruMem); | 
| andrewm@0 | 273 		pru_buffer_spi_dac = (uint16_t *)&pruMem[PRU_MEM_DAC_OFFSET/sizeof(uint32_t)]; | 
| andrewm@0 | 274 | 
| andrewm@12 | 275 		/* ADC memory starts after N(ch)*2(buffers)*bufsize samples */ | 
| andrewm@12 | 276 		pru_buffer_spi_adc = &pru_buffer_spi_dac[2 * spi_num_channels * spi_buffer_frames]; | 
| andrewm@0 | 277 	} | 
| andrewm@0 | 278 	else { | 
| andrewm@0 | 279 		pru_buffer_spi_dac = pru_buffer_spi_adc = 0; | 
| andrewm@0 | 280 	} | 
| andrewm@0 | 281 | 
| andrewm@0 | 282     /* Set up flags */ | 
| andrewm@0 | 283     pru_buffer_comm[PRU_SHOULD_STOP] = 0; | 
| andrewm@0 | 284     pru_buffer_comm[PRU_CURRENT_BUFFER] = 0; | 
| andrewm@0 | 285     pru_buffer_comm[PRU_BUFFER_FRAMES] = spi_buffer_frames; | 
| andrewm@0 | 286     pru_buffer_comm[PRU_SHOULD_SYNC] = 0; | 
| andrewm@0 | 287     pru_buffer_comm[PRU_SYNC_ADDRESS] = 0; | 
| andrewm@0 | 288     pru_buffer_comm[PRU_SYNC_PIN_MASK] = 0; | 
| andrewm@0 | 289     if(led_enabled) { | 
| andrewm@0 | 290     	pru_buffer_comm[PRU_LED_ADDRESS] = USERLED3_GPIO_BASE; | 
| andrewm@0 | 291     	pru_buffer_comm[PRU_LED_PIN_MASK] = USERLED3_PIN_MASK; | 
| andrewm@0 | 292     } | 
| andrewm@0 | 293     else { | 
| andrewm@0 | 294     	pru_buffer_comm[PRU_LED_ADDRESS] = 0; | 
| andrewm@0 | 295     	pru_buffer_comm[PRU_LED_PIN_MASK] = 0; | 
| andrewm@0 | 296     } | 
| andrewm@0 | 297     if(spi_enabled) { | 
| andrewm@0 | 298     	pru_buffer_comm[PRU_USE_SPI] = 1; | 
| andrewm@12 | 299     	pru_buffer_comm[PRU_SPI_NUM_CHANNELS] = spi_num_channels; | 
| andrewm@0 | 300     } | 
| andrewm@0 | 301     else { | 
| andrewm@0 | 302     	pru_buffer_comm[PRU_USE_SPI] = 0; | 
| andrewm@12 | 303     	pru_buffer_comm[PRU_SPI_NUM_CHANNELS] = 0; | 
| andrewm@0 | 304     } | 
| andrewm@0 | 305 | 
| andrewm@0 | 306     /* Clear ADC and DAC memory */ | 
| andrewm@0 | 307     if(spi_enabled) { | 
| andrewm@0 | 308 		for(int i = 0; i < PRU_MEM_DAC_LENGTH / 2; i++) | 
| andrewm@0 | 309 			pru_buffer_spi_dac[i] = 0; | 
| andrewm@0 | 310     } | 
| andrewm@0 | 311 	for(int i = 0; i < PRU_MEM_MCASP_LENGTH / 2; i++) | 
| andrewm@0 | 312 		pru_buffer_audio_dac[i] = 0; | 
| andrewm@0 | 313 | 
| andrewm@0 | 314 	/* If using GPIO test pin for Xenomai (for debugging), initialise the pointer now */ | 
| andrewm@0 | 315 	if(xenomai_test_pin && xenomai_gpio_fd < 0) { | 
| andrewm@0 | 316 		xenomai_gpio_fd = open("/dev/mem", O_RDWR); | 
| andrewm@0 | 317 		if(xenomai_gpio_fd < 0) | 
| andrewm@0 | 318 			rt_printf("Unable to open /dev/mem for GPIO test pin\n"); | 
| andrewm@0 | 319 		else { | 
| andrewm@0 | 320 			xenomai_gpio = (uint32_t *)mmap(0, GPIO_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, xenomai_gpio_fd, TEST_PIN_GPIO_BASE); | 
| andrewm@0 | 321 			if(xenomai_gpio == MAP_FAILED) { | 
| andrewm@0 | 322 				rt_printf("Unable to map GPIO address for test pin\n"); | 
| andrewm@0 | 323 				xenomai_gpio = 0; | 
| andrewm@0 | 324 				close(xenomai_gpio_fd); | 
| andrewm@0 | 325 				xenomai_gpio_fd = -1; | 
| andrewm@0 | 326 			} | 
| andrewm@0 | 327 		} | 
| andrewm@0 | 328 	} | 
| andrewm@0 | 329 | 
| andrewm@0 | 330 	return 0; | 
| andrewm@0 | 331 } | 
| andrewm@0 | 332 | 
| andrewm@0 | 333 // Run the code image in the specified file | 
| andrewm@0 | 334 int PRU::start(char * const filename) | 
| andrewm@0 | 335 { | 
| andrewm@0 | 336 	/* Clear any old interrupt */ | 
| andrewm@0 | 337 	prussdrv_pru_clear_event(pru_number == 0 ? PRU0_ARM_INTERRUPT : PRU1_ARM_INTERRUPT); | 
| andrewm@0 | 338 | 
| andrewm@0 | 339     /* Load and execute binary on PRU */ | 
| andrewm@0 | 340     if(prussdrv_exec_program(pru_number, filename)) { | 
| andrewm@0 | 341     	rt_printf("Failed to execute PRU code from %s\n", filename); | 
| andrewm@0 | 342     	return 1; | 
| andrewm@0 | 343     } | 
| andrewm@0 | 344 | 
| andrewm@0 | 345     running = true; | 
| andrewm@0 | 346     return 0; | 
| andrewm@0 | 347 } | 
| andrewm@0 | 348 | 
| andrewm@0 | 349 // Main loop to read and write data from/to PRU | 
| andrewm@0 | 350 void PRU::loop() | 
| andrewm@0 | 351 { | 
| andrewm@0 | 352 	// Polling interval is 1/4 of the period | 
| andrewm@12 | 353 	RTIME sleepTime = PRU_SAMPLE_INTERVAL_NS * (spi_num_channels / 2) * spi_buffer_frames / 4; | 
| andrewm@0 | 354 	float *audioInBuffer, *audioOutBuffer; | 
| andrewm@0 | 355 | 
| andrewm@0 | 356 	audioInBuffer = (float *)malloc(2 * audio_buffer_frames * sizeof(float)); | 
| andrewm@0 | 357 	audioOutBuffer = (float *)malloc(2 * audio_buffer_frames * sizeof(float)); | 
| andrewm@0 | 358 | 
| andrewm@0 | 359 	if(audioInBuffer == 0 || audioOutBuffer == 0) { | 
| andrewm@0 | 360 		rt_printf("Error: couldn't allocated audio buffers\n"); | 
| andrewm@0 | 361 		return; | 
| andrewm@0 | 362 	} | 
| andrewm@0 | 363 | 
| andrewm@0 | 364 	while(!gShouldStop) { | 
| andrewm@0 | 365 		// Wait for PRU to move to buffer 1 | 
| andrewm@0 | 366 		while(pru_buffer_comm[PRU_CURRENT_BUFFER] == 0 && !gShouldStop) { | 
| andrewm@0 | 367 			rt_task_sleep(sleepTime); | 
| andrewm@0 | 368 		} | 
| andrewm@0 | 369 		if(gShouldStop) | 
| andrewm@0 | 370 			break; | 
| andrewm@0 | 371 | 
| andrewm@0 | 372 		if(xenomai_gpio != 0) { | 
| andrewm@0 | 373 			// Set the test pin high | 
| andrewm@0 | 374 			xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN_MASK; | 
| andrewm@0 | 375 		} | 
| andrewm@0 | 376 | 
| andrewm@0 | 377 		// Render from/to buffer 0 | 
| andrewm@0 | 378 | 
| andrewm@0 | 379 		// Convert short (16-bit) samples to float | 
| andrewm@0 | 380 		for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++) | 
| andrewm@0 | 381 			audioInBuffer[n] = (float)pru_buffer_audio_adc[n] / 32768.0; | 
| andrewm@0 | 382 | 
| andrewm@0 | 383 		if(spi_enabled) | 
| andrewm@0 | 384 			render(spi_buffer_frames, audio_buffer_frames, audioInBuffer, audioOutBuffer, | 
| andrewm@0 | 385 					pru_buffer_spi_adc, pru_buffer_spi_dac); | 
| andrewm@0 | 386 		else | 
| andrewm@0 | 387 			render(0, audio_buffer_frames, audioInBuffer, audioOutBuffer, 0, 0); | 
| andrewm@0 | 388 | 
| andrewm@0 | 389 		// Convert float back to short | 
| andrewm@0 | 390 		for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++) { | 
| andrewm@0 | 391 			int out = audioOutBuffer[n] * 32768.0; | 
| andrewm@0 | 392 			if(out < -32768) out = -32768; | 
| andrewm@0 | 393 			else if(out > 32767) out = 32767; | 
| andrewm@0 | 394 			pru_buffer_audio_dac[n] = (int16_t)out; | 
| andrewm@0 | 395 		} | 
| andrewm@0 | 396 | 
| andrewm@0 | 397 		if(xenomai_gpio != 0) { | 
| andrewm@0 | 398 			// Set the test pin high | 
| andrewm@0 | 399 			xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN_MASK; | 
| andrewm@0 | 400 		} | 
| andrewm@0 | 401 | 
| andrewm@0 | 402 		// Wait for PRU to move to buffer 0 | 
| andrewm@0 | 403 		while(pru_buffer_comm[PRU_CURRENT_BUFFER] != 0 && !gShouldStop) { | 
| andrewm@0 | 404 			rt_task_sleep(sleepTime); | 
| andrewm@0 | 405 		} | 
| andrewm@0 | 406 | 
| andrewm@0 | 407 		if(gShouldStop) | 
| andrewm@0 | 408 			break; | 
| andrewm@0 | 409 | 
| andrewm@0 | 410 		if(xenomai_gpio != 0) { | 
| andrewm@0 | 411 			// Set the test pin high | 
| andrewm@0 | 412 			xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN_MASK; | 
| andrewm@0 | 413 		} | 
| andrewm@0 | 414 | 
| andrewm@0 | 415 		// Render from/to buffer 1 | 
| andrewm@0 | 416 | 
| andrewm@0 | 417 		// Convert short (16-bit) samples to float | 
| andrewm@0 | 418 		for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++) | 
| andrewm@0 | 419 			audioInBuffer[n] = (float)pru_buffer_audio_adc[n + audio_buffer_frames * 2] / 32768.0; | 
| andrewm@0 | 420 | 
| andrewm@0 | 421 		if(spi_enabled) | 
| andrewm@0 | 422 			render(spi_buffer_frames, audio_buffer_frames, audioInBuffer, audioOutBuffer, | 
| andrewm@12 | 423 					&pru_buffer_spi_adc[spi_buffer_frames * spi_num_channels], &pru_buffer_spi_dac[spi_buffer_frames * spi_num_channels]); | 
| andrewm@0 | 424 		else | 
| andrewm@0 | 425 			render(0, audio_buffer_frames, audioInBuffer, audioOutBuffer, 0, 0); | 
| andrewm@0 | 426 | 
| andrewm@0 | 427 		// Convert float back to short | 
| andrewm@0 | 428 		for(unsigned int n = 0; n < 2 * audio_buffer_frames; n++) { | 
| andrewm@0 | 429 			int out = audioOutBuffer[n] * 32768.0; | 
| andrewm@0 | 430 			if(out < -32768) out = -32768; | 
| andrewm@0 | 431 			else if(out > 32767) out = 32767; | 
| andrewm@0 | 432 			pru_buffer_audio_dac[n + audio_buffer_frames * 2] = (int16_t)out; | 
| andrewm@0 | 433 		} | 
| andrewm@0 | 434 | 
| andrewm@0 | 435 		if(xenomai_gpio != 0) { | 
| andrewm@0 | 436 			// Set the test pin high | 
| andrewm@0 | 437 			xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN_MASK; | 
| andrewm@0 | 438 		} | 
| andrewm@0 | 439 	} | 
| andrewm@0 | 440 | 
| andrewm@0 | 441 	// Tell PRU to stop | 
| andrewm@0 | 442 	pru_buffer_comm[PRU_SHOULD_STOP] = 1; | 
| andrewm@0 | 443 | 
| andrewm@0 | 444 	free(audioInBuffer); | 
| andrewm@0 | 445 	free(audioOutBuffer); | 
| andrewm@0 | 446 } | 
| andrewm@0 | 447 | 
| andrewm@0 | 448 // Wait for an interrupt from the PRU indicate it is finished | 
| andrewm@0 | 449 void PRU::waitForFinish() | 
| andrewm@0 | 450 { | 
| andrewm@0 | 451 	if(!running) | 
| andrewm@0 | 452 		return; | 
| andrewm@0 | 453     prussdrv_pru_wait_event (PRU_EVTOUT_0); | 
| andrewm@0 | 454     prussdrv_pru_clear_event(pru_number == 0 ? PRU0_ARM_INTERRUPT : PRU1_ARM_INTERRUPT); | 
| andrewm@0 | 455 } | 
| andrewm@0 | 456 | 
| andrewm@0 | 457 // Turn off the PRU when done | 
| andrewm@0 | 458 void PRU::disable() | 
| andrewm@0 | 459 { | 
| andrewm@0 | 460     /* Disable PRU and close memory mapping*/ | 
| andrewm@0 | 461     prussdrv_pru_disable(pru_number); | 
| andrewm@0 | 462     prussdrv_exit(); | 
| andrewm@0 | 463 	running = false; | 
| andrewm@0 | 464 } | 
| andrewm@0 | 465 | 
| andrewm@0 | 466 // Debugging | 
| andrewm@0 | 467 void PRU::setGPIOTestPin() | 
| andrewm@0 | 468 { | 
| andrewm@0 | 469 	if(!xenomai_gpio) | 
| andrewm@0 | 470 		return; | 
| andrewm@0 | 471 	xenomai_gpio[GPIO_SETDATAOUT] = TEST_PIN2_MASK; | 
| andrewm@0 | 472 } | 
| andrewm@0 | 473 | 
| andrewm@0 | 474 void PRU::clearGPIOTestPin() | 
| andrewm@0 | 475 { | 
| andrewm@0 | 476 	if(!xenomai_gpio) | 
| andrewm@0 | 477 		return; | 
| andrewm@0 | 478 	xenomai_gpio[GPIO_CLEARDATAOUT] = TEST_PIN2_MASK; | 
| andrewm@0 | 479 } |