Mercurial > hg > beaglert
changeset 12:a6beeba3a648
Initial support for higher matrix sample rates by reducing the number of channels. Input not tested yet, and not all examples updated to new format.
author | andrewm |
---|---|
date | Thu, 22 Jan 2015 19:00:22 +0000 |
parents | 517715b23df0 |
children | 6adb088196a7 |
files | .cproject core/PRU.cpp core/RTAudio.cpp core/RTAudioCommandLine.cpp include/PRU.h include/RTAudio.h include/render.h projects/basic/render.cpp projects/basic_analog_output/render.cpp projects/basic_sensor/render.cpp pru_rtaudio.bin pru_rtaudio.p |
diffstat | 12 files changed, 178 insertions(+), 78 deletions(-) [+] |
line wrap: on
line diff
--- a/.cproject Thu Nov 13 16:02:59 2014 +0100 +++ b/.cproject Thu Jan 22 19:00:22 2015 +0000 @@ -49,17 +49,8 @@ </tool> <tool id="cdt.managedbuild.tool.gnu.c.linker.exe.debug.214461086" name="GCC C Linker" superClass="cdt.managedbuild.tool.gnu.c.linker.exe.debug"/> <tool command="arm-linux-gnueabihf-g++" id="cdt.managedbuild.tool.gnu.cpp.linker.exe.debug.1669966018" name="GCC C++ Linker" superClass="cdt.managedbuild.tool.gnu.cpp.linker.exe.debug"> - <option id="gnu.cpp.link.option.paths.462980690" name="Library search path (-L)" superClass="gnu.cpp.link.option.paths" valueType="libPaths"> - <listOptionValue builtIn="false" value="/usr/arm-linux-gnueabihf/lib"/> - <listOptionValue builtIn="false" value="/usr/arm-linux-gnueabihf/lib/xenomai"/> - <listOptionValue builtIn="false" value="/usr/lib/x86_64-linux-gnu"/> - </option> - <option id="gnu.cpp.link.option.libs.139390951" name="Libraries (-l)" superClass="gnu.cpp.link.option.libs" valueType="libs"> - <listOptionValue builtIn="false" value="rt"/> - <listOptionValue builtIn="false" value="native"/> - <listOptionValue builtIn="false" value="xenomai"/> - <listOptionValue builtIn="false" value="sndfile"/> - </option> + <option id="gnu.cpp.link.option.paths.462980690" name="Library search path (-L)" superClass="gnu.cpp.link.option.paths"/> + <option id="gnu.cpp.link.option.libs.139390951" name="Libraries (-l)" superClass="gnu.cpp.link.option.libs"/> <option id="gnu.cpp.link.option.flags.2096887116" name="Linker flags" superClass="gnu.cpp.link.option.flags" value="-pthread -rdynamics" valueType="string"/> <option id="gnu.cpp.link.option.userobjs.537608578" name="Other objects" superClass="gnu.cpp.link.option.userobjs" valueType="userObjs"> <listOptionValue builtIn="false" value=""${workspace_loc:/${ProjName}/libprussdrv.a}""/> @@ -82,7 +73,7 @@ <sourceEntries> <entry excluding="audio_routines_old.S" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="core"/> <entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="include"/> - <entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="projects/tank_wars"/> + <entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="projects/basic_analog_output"/> </sourceEntries> </configuration> </storageModule> @@ -156,7 +147,7 @@ <sourceEntries> <entry excluding="audio_routines_old.S" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="core"/> <entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="include"/> - <entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="projects/tank_wars"/> + <entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name="projects/basic_analog_output"/> </sourceEntries> </configuration> </storageModule>
--- a/core/PRU.cpp Thu Nov 13 16:02:59 2014 +0100 +++ b/core/PRU.cpp Thu Jan 22 19:00:22 2015 +0000 @@ -50,8 +50,9 @@ #define PRU_LED_PIN_MASK 7 #define PRU_FRAME_COUNT 8 #define PRU_USE_SPI 9 +#define PRU_SPI_NUM_CHANNELS 10 -#define PRU_SAMPLE_INTERVAL_NS 45351 // 22050Hz per SPI sample = 45.351us +#define PRU_SAMPLE_INTERVAL_NS 11338 // 88200Hz per SPI sample = 11.338us #define GPIO0_ADDRESS 0x44E07000 #define GPIO1_ADDRESS 0x4804C000 @@ -79,7 +80,7 @@ // 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), xenomai_gpio_fd(-1), xenomai_gpio(0) + gpio_test_pin_enabled(false), spi_num_channels(0), xenomai_gpio_fd(-1), xenomai_gpio(0) { } @@ -224,7 +225,7 @@ } // Initialise and open the PRU -int PRU::initialise(int pru_num, int frames_per_buffer, bool xenomai_test_pin) +int PRU::initialise(int pru_num, int frames_per_buffer, int spi_channels, bool xenomai_test_pin) { uint32_t *pruMem = 0; @@ -235,6 +236,13 @@ 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; @@ -250,22 +258,22 @@ prussdrv_pruintc_init(&pruss_intc_initdata); spi_buffer_frames = frames_per_buffer; - audio_buffer_frames = spi_buffer_frames * 2; + audio_buffer_frames = spi_buffer_frames * spi_num_channels / 4; /* 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)*2(samples/spi)*bufsize samples later */ - pru_buffer_audio_adc = &pru_buffer_audio_dac[8 * spi_buffer_frames]; + /* 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 8(ch)*2(buffers)*bufsize samples */ - pru_buffer_spi_adc = &pru_buffer_spi_dac[16 * spi_buffer_frames]; + /* 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; @@ -288,9 +296,11 @@ } 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; } /* Clear ADC and DAC memory */ @@ -340,7 +350,7 @@ void PRU::loop() { // Polling interval is 1/4 of the period - RTIME sleepTime = PRU_SAMPLE_INTERVAL_NS * spi_buffer_frames / 4; + RTIME sleepTime = PRU_SAMPLE_INTERVAL_NS * (spi_num_channels / 2) * spi_buffer_frames / 4; float *audioInBuffer, *audioOutBuffer; audioInBuffer = (float *)malloc(2 * audio_buffer_frames * sizeof(float)); @@ -410,7 +420,7 @@ if(spi_enabled) render(spi_buffer_frames, audio_buffer_frames, audioInBuffer, audioOutBuffer, - &pru_buffer_spi_adc[spi_buffer_frames * 8], &pru_buffer_spi_dac[spi_buffer_frames * 8]); + &pru_buffer_spi_adc[spi_buffer_frames * spi_num_channels], &pru_buffer_spi_dac[spi_buffer_frames * spi_num_channels]); else render(0, audio_buffer_frames, audioInBuffer, audioOutBuffer, 0, 0);
--- a/core/RTAudio.cpp Thu Nov 13 16:02:59 2014 +0100 +++ b/core/RTAudio.cpp Thu Jan 22 19:00:22 2015 +0000 @@ -67,7 +67,8 @@ // periodSize indicates the number of _sensor_ frames per period: the audio period size // is twice this value. In total, the audio latency in frames will be 4*periodSize, // plus any latency inherent in the ADCs and DACs themselves. -// useMatrix indicates whether to use the ADC and DAC or just the audio codec. +// useMatrix indicates whether to enable the ADC and DAC or just use the audio codec. +// numMatrixChannels indicates how many ADC and DAC channels to use. // userData is an opaque pointer which will be passed through to the initialise_render() // function for application-specific use // @@ -114,6 +115,24 @@ } } + // Limit the matrix channels to sane values + if(settings->numMatrixChannels >= 8) + settings->numMatrixChannels = 8; + else if(settings->numMatrixChannels >= 4) + settings->numMatrixChannels = 4; + else + settings->numMatrixChannels = 2; + + // Sanity check the combination of channels and period size + if(settings->numMatrixChannels <= 4 && settings->periodSize < 2) { + cout << "Error: " << settings->numMatrixChannels << " channels and period size of " << settings->periodSize << " not supported.\n"; + return 1; + } + if(settings->numMatrixChannels <= 2 && settings->periodSize < 4) { + cout << "Error: " << settings->numMatrixChannels << " channels and period size of " << settings->periodSize << " not supported.\n"; + return 1; + } + // Use PRU for audio gPRU = new PRU(); gAudioCodec = new I2c_Codec(); @@ -122,7 +141,7 @@ cout << "Error: unable to prepare GPIO for PRU audio\n"; return 1; } - if(gPRU->initialise(0, settings->periodSize, true)) { + if(gPRU->initialise(0, settings->periodSize, settings->numMatrixChannels, true)) { cout << "Error: unable to initialise PRU\n"; return 1; } @@ -140,7 +159,23 @@ BeagleRT_setADCLevel(settings->adcLevel); BeagleRT_setHeadphoneLevel(settings->headphoneLevel); - if(!initialise_render(2, settings->useMatrix ? settings->periodSize : 0, settings->periodSize * 2, 22050.0, 44100.0, userData)) { + // Initialise the rendering environment: pass the number of audio and matrix + // channels, the period size for matrix and audio, and the sample rates + + int audioPeriodSize = settings->periodSize * 2; + float audioSampleRate = 44100.0; + float matrixSampleRate = 22050.0; + if(settings->useMatrix) { + audioPeriodSize = settings->periodSize * settings->numMatrixChannels / 4; + matrixSampleRate = audioSampleRate * 4.0 / (float)settings->numMatrixChannels; + } + + if(!initialise_render(settings->useMatrix ? settings->numMatrixChannels : 0, /* matrix channels */ + 2, /* audio channels */ + settings->useMatrix ? settings->periodSize : 0, /* matrix period size */ + audioPeriodSize, + matrixSampleRate, audioSampleRate, + userData)) { cout << "Couldn't initialise audio rendering\n"; return 1; }
--- a/core/RTAudioCommandLine.cpp Thu Nov 13 16:02:59 2014 +0100 +++ b/core/RTAudioCommandLine.cpp Thu Jan 22 19:00:22 2015 +0000 @@ -18,6 +18,7 @@ {"period", 1, NULL, 'p'}, {"verbose", 0, NULL, 'v'}, {"use-matrix", 1, NULL, 'm'}, + {"matrix-channels", 1, NULL, 'C'}, {"mute-speaker", 1, NULL, 'M'}, {"dac-level", 1, NULL, 'D'}, {"adc-level", 1, NULL, 'A'}, @@ -25,7 +26,7 @@ {NULL, 0, NULL, 0} }; -const char gDefaultShortOptions[] = "p:vm:M:D:A:H:"; +const char gDefaultShortOptions[] = "p:vm:M:C:D:A:H:"; // This function sets the default settings for the RTAudioSettings structure void BeagleRT_defaultSettings(RTAudioSettings *settings) @@ -37,6 +38,7 @@ settings->adcLevel = DEFAULT_ADC_LEVEL; settings->headphoneLevel = DEFAULT_HP_LEVEL; settings->useMatrix = 1; + settings->numMatrixChannels = 8; settings->verbose = 0; settings->codecI2CAddress = CODEC_I2C_ADDRESS; settings->ampMutePin = kAmplifierMutePin; @@ -123,6 +125,15 @@ case 'm': settings->useMatrix = atoi(optarg); break; + case 'C': + settings->numMatrixChannels = atoi(optarg); + if(settings->numMatrixChannels >= 8) + settings->numMatrixChannels = 8; + else if(settings->numMatrixChannels >= 4) + settings->numMatrixChannels = 4; + else + settings->numMatrixChannels = 2; + break; case 'M': settings->beginMuted = atoi(optarg); break; @@ -146,11 +157,12 @@ // Call from within your own usage function void BeagleRT_usage() { - std::cerr << " --period [-p] period: Set the hardware period (buffer) size in matrix samples\n"; - std::cerr << " --dac-level [-D] dBs: Set the DAC output level (0dB max; -63.5dB min)\n"; - std::cerr << " --adc-level [-A] dBs: Set the ADC input level (0dB max; -12dB min)\n"; - std::cerr << " --hp-level [-H] dBs: Set the headphone output level (0dB max; -63.5dB min)\n"; - std::cerr << " --mute-speaker [-M] val: Set whether to mute the speaker initially (default: no)\n"; - std::cerr << " --use-matrix [-m] val: Set whether to use ADC/DAC matrix\n"; - std::cerr << " --verbose [-v]: Enable verbose logging information\n"; + std::cerr << " --period [-p] period: Set the hardware period (buffer) size in matrix samples\n"; + std::cerr << " --dac-level [-D] dBs: Set the DAC output level (0dB max; -63.5dB min)\n"; + std::cerr << " --adc-level [-A] dBs: Set the ADC input level (0dB max; -12dB min)\n"; + std::cerr << " --hp-level [-H] dBs: Set the headphone output level (0dB max; -63.5dB min)\n"; + std::cerr << " --mute-speaker [-M] val: Set whether to mute the speaker initially (default: no)\n"; + std::cerr << " --use-matrix [-m] val: Set whether to use ADC/DAC matrix\n"; + std::cerr << " --matrix-channels [-C] val: Set the number of ADC/DAC channels (default: 8)\n"; + std::cerr << " --verbose [-v]: Enable verbose logging information\n"; }
--- a/include/PRU.h Thu Nov 13 16:02:59 2014 +0100 +++ b/include/PRU.h Thu Jan 22 19:00:22 2015 +0000 @@ -33,7 +33,8 @@ void cleanupGPIO(); // Initialise and open the PRU - int initialise(int pru_num, int frames_per_buffer, bool xenomai_test_pin = false); + int initialise(int pru_num, int frames_per_buffer, int spi_channels, + bool xenomai_test_pin = false); // Run the code image in the specified file int start(char * const filename); @@ -58,6 +59,7 @@ bool gpio_enabled; // Whether GPIO has been prepared bool led_enabled; // Whether a user LED is enabled bool gpio_test_pin_enabled; // Whether the test pin was also enabled + int spi_num_channels; // How many channels to use for SPI ADC/DAC volatile uint32_t *pru_buffer_comm; uint16_t *pru_buffer_spi_dac;
--- a/include/RTAudio.h Thu Nov 13 16:02:59 2014 +0100 +++ b/include/RTAudio.h Thu Jan 22 19:00:22 2015 +0000 @@ -43,7 +43,8 @@ float dacLevel; // Level for the audio DAC output float adcLevel; // Level for the audio ADC input float headphoneLevel; // Level for the headphone output - int useMatrix; // Whether to enable the ADC and DAC + int useMatrix; // Whether to use the matrix + int numMatrixChannels; // How many channels for the ADC and DAC int verbose; // Whether to use verbose logging // These items are hardware-dependent and should only be changed
--- a/include/render.h Thu Nov 13 16:02:59 2014 +0100 +++ b/include/render.h Thu Jan 22 19:00:22 2015 +0000 @@ -33,7 +33,8 @@ #define MATRIX_MAX 65535.0 -bool initialise_render(int numChannels, int numMatrixFramesPerPeriod, +bool initialise_render(int numMatrixChannels, int numAudioChannels, + int numMatrixFramesPerPeriod, int numAudioFramesPerPeriod, float matrixSampleRate, float audioSampleRate, void *userData);
--- a/projects/basic/render.cpp Thu Nov 13 16:02:59 2014 +0100 +++ b/projects/basic/render.cpp Thu Jan 22 19:00:22 2015 +0000 @@ -23,14 +23,16 @@ // // Return true on success; returning false halts the program. -bool initialise_render(int numChannels, int numMatrixFramesPerPeriod, - int numAudioFramesPerPeriod, float matrixSampleRate, - float audioSampleRate, void *userData) +bool initialise_render(int numMatrixChannels, int numAudioChannels, + int numMatrixFramesPerPeriod, + int numAudioFramesPerPeriod, + float matrixSampleRate, float audioSampleRate, + void *userData) { // Retrieve a parameter passed in from the initAudio() call gFrequency = *(float *)userData; - gNumChannels = numChannels; + gNumChannels = numAudioChannels; gInverseSampleRate = 1.0 / audioSampleRate; gPhase = 0.0;
--- a/projects/basic_analog_output/render.cpp Thu Nov 13 16:02:59 2014 +0100 +++ b/projects/basic_analog_output/render.cpp Thu Jan 22 19:00:22 2015 +0000 @@ -19,6 +19,8 @@ float gPhase; float gInverseSampleRate; +int gMatrixChannels; + // initialise_render() is called once before the audio rendering starts. // Use it to perform any initialisation and allocation which is dependent // on the period size or sample rate. @@ -28,19 +30,22 @@ // // Return true on success; returning false halts the program. -bool initialise_render(int numChannels, int numMatrixFramesPerPeriod, - int numAudioFramesPerPeriod, float matrixSampleRate, - float audioSampleRate, void *userData) +bool initialise_render(int numMatrixChannels, int numAudioChannels, + int numMatrixFramesPerPeriod, + int numAudioFramesPerPeriod, + float matrixSampleRate, float audioSampleRate, + void *userData) { // Retrieve a parameter passed in from the initAudio() call gFrequency = *(float *)userData; - if(numMatrixFramesPerPeriod*2 != numAudioFramesPerPeriod) { - rt_printf("Error: this example needs the matrix enabled, running at half audio rate\n"); + if(numMatrixFramesPerPeriod == 0) { + rt_printf("Error: this example needs the matrix enabled\n"); return false; } - gInverseSampleRate = 1.0 / audioSampleRate; + gMatrixChannels = numMatrixChannels; + gInverseSampleRate = 1.0 / matrixSampleRate; gPhase = 0.0; return true; @@ -57,12 +62,13 @@ for(int n = 0; n < numMatrixFrames; n++) { // Set LED to different phase for each matrix channel float relativePhase = 0.0; - for(int channel = 0; channel < 8; channel++) { + for(int channel = 0; channel < gMatrixChannels; channel++) { float out = kMinimumAmplitude + kAmplitudeRange * 0.5f * (1.0f + sinf(gPhase + relativePhase)); if(out > MATRIX_MAX) out = MATRIX_MAX; - analogWrite(channel, n, out); + matrixOut[n * gMatrixChannels + channel] = (uint16_t)out; + //analogWrite(channel, n, out); // Advance by pi/4 (1/8 of a full rotation) for each channel relativePhase += M_PI * 0.25;
--- a/projects/basic_sensor/render.cpp Thu Nov 13 16:02:59 2014 +0100 +++ b/projects/basic_sensor/render.cpp Thu Jan 22 19:00:22 2015 +0000 @@ -31,16 +31,18 @@ // // Return true on success; returning false halts the program. -bool initialise_render(int numChannels, int numMatrixFramesPerPeriod, - int numAudioFramesPerPeriod, float matrixSampleRate, - float audioSampleRate, void *userData) +bool initialise_render(int numMatrixChannels, int numAudioChannels, + int numMatrixFramesPerPeriod, + int numAudioFramesPerPeriod, + float matrixSampleRate, float audioSampleRate, + void *userData) { if(numMatrixFramesPerPeriod*2 != numAudioFramesPerPeriod) { rt_printf("Error: this example needs the matrix enabled, running at half audio rate\n"); return false; } - gNumChannels = numChannels; + gNumChannels = numAudioChannels; gInverseSampleRate = 1.0 / audioSampleRate; gPhase = 0.0;
--- a/pru_rtaudio.p Thu Nov 13 16:02:59 2014 +0100 +++ b/pru_rtaudio.p Thu Jan 22 19:00:22 2015 +0000 @@ -80,6 +80,7 @@ #define COMM_LED_PIN_MASK 28 // Which pin to write to change LED #define COMM_FRAME_COUNT 32 // How many frames have elapse since beginning #define COMM_USE_SPI 36 // Whether or not to use SPI ADC and DAC +#define COMM_NUM_CHANNELS 40 // Low 2 bits indicate 8 [0x3], 4 [0x1] or 2 [0x0] channels #define MCASP0_BASE 0x48038000 #define MCASP1_BASE 0x4803C000 @@ -173,15 +174,17 @@ #define MCASP_DATA_MASK 0xFFFF // 16 bit data #define MCASP_DATA_FORMAT 0x807C // MSB first, 0 bit delay, 16 bits, CFG bus, ROR 16bits -#define C_MCASP_MEM C28 // Shared PRU mem +#define C_MCASP_MEM C28 // Shared PRU mem // Flags for the flags register #define FLAG_BIT_BUFFER1 0 #define FLAG_BIT_USE_SPI 1 +#define FLAG_BIT_MCASP_HWORD 2 // Whether we are on the high word for McASP transmission // Registers used throughout // r1, r2, r3 are used for temporary storage +#define reg_num_channels r9 // Number of SPI ADC/DAC channels to use #define reg_frame_current r10 // Current frame count in SPI ADC/DAC transfer #define reg_frame_total r11 // Total frame count for SPI ADC/DAC #define reg_dac_data r12 // Current dword for SPI DAC @@ -356,6 +359,9 @@ // Clear flags MOV reg_flags, 0 + // Default number of channels in case SPI disabled + LDI reg_num_channels, 8 + // Find out whether we should use SPI ADC and DAC LBBO r2, reg_comm_addr, COMM_USE_SPI, 4 QBEQ SPI_FLAG_CHECK_DONE, r2, 0 @@ -364,6 +370,19 @@ SPI_FLAG_CHECK_DONE: // If we don't use SPI, then skip all this init QBBC SPI_INIT_DONE, reg_flags, FLAG_BIT_USE_SPI + + // Load the number of channels: valid values are 8, 4 or 2 + LBBO reg_num_channels, reg_comm_addr, COMM_NUM_CHANNELS, 4 + QBGT SPI_NUM_CHANNELS_LT8, reg_num_channels, 8 // 8 > num_channels ? + LDI reg_num_channels, 8 // If N >= 8, N = 8 + QBA SPI_NUM_CHANNELS_DONE +SPI_NUM_CHANNELS_LT8: + QBGT SPI_NUM_CHANNELS_LT4, reg_num_channels, 4 // 4 > num_channels ? + LDI reg_num_channels, 4 // If N >= 4, N = 4 + QBA SPI_NUM_CHANNELS_DONE +SPI_NUM_CHANNELS_LT4: + LDI reg_num_channels, 2 // else N = 2 +SPI_NUM_CHANNELS_DONE: // Init SPI clock MOV r2, 0x02 @@ -491,11 +510,13 @@ MCASP_REG_SET_BIT_AND_POLL MCASP_XGBLCTL, (1 << 12) // Set XFRST // Initialisation -LBBO reg_frame_total, reg_comm_addr, COMM_BUFFER_FRAMES, 4 // Total frame count (SPI; 2x for McASP) +LBBO reg_frame_total, reg_comm_addr, COMM_BUFFER_FRAMES, 4 // Total frame count (SPI; 0.5x-2x for McASP) MOV reg_dac_buf0, 0 // DAC buffer 0 start pointer -LSL reg_dac_buf1, reg_frame_total, 4 // DAC buffer 1 start pointer = 8[ch]*2[bytes]*bufsize +LSL reg_dac_buf1, reg_frame_total, 1 // DAC buffer 1 start pointer = N[ch]*2[bytes]*bufsize +LMBD r2, reg_num_channels, 1 // Returns 1, 2 or 3 depending on the number of channels +LSL reg_dac_buf1, reg_dac_buf1, r2 // Multiply by 2, 4 or 8 to get the N[ch] scaling above MOV reg_mcasp_buf0, 0 // McASP DAC buffer 0 start pointer -LSL reg_mcasp_buf1, reg_frame_total, 3 // McASP DAC buffer 1 start pointer = 2[ch]*2[bytes]*2[samples/spi]*bufsize +LSL reg_mcasp_buf1, reg_frame_total, r2 // McASP DAC buffer 1 start pointer = 2[ch]*2[bytes]*(N/4)[samples/spi]*bufsize CLR reg_flags, reg_flags, FLAG_BIT_BUFFER1 // Bit 0 holds which buffer we are on MOV r2, 0 SBBO r2, reg_comm_addr, COMM_FRAME_COUNT, 4 // Start with frame count of 0 @@ -521,16 +542,19 @@ // Write a single buffer of DAC samples and read a buffer of ADC samples // Load starting positions MOV reg_dac_current, reg_dac_buf0 // DAC: reg_dac_current is current pointer - LSL reg_adc_current, reg_frame_total, 5 // 16 * 2 * bufsize - ADD reg_adc_current, reg_adc_current, reg_dac_current // ADC: starts 16 * 2 * bufsize beyond DAC + LMBD r2, reg_num_channels, 1 // 1, 2 or 3 for 2, 4 or 8 channels + LSL reg_adc_current, reg_frame_total, r2 + LSL reg_adc_current, reg_adc_current, 2 // N * 2 * 2 * bufsize + ADD reg_adc_current, reg_adc_current, reg_dac_current // ADC: starts N * 2 * 2 * bufsize beyond DAC MOV reg_mcasp_dac_current, reg_mcasp_buf0 // McASP: set current DAC pointer - LSL reg_mcasp_adc_current, reg_frame_total, 4 // McASP ADC: starts 4*2*2*bufsize beyond DAC + LSL reg_mcasp_adc_current, reg_frame_total, r2 // McASP ADC: starts (N/2)*2*2*bufsize beyond DAC + LSL reg_mcasp_adc_current, reg_mcasp_adc_current, 1 ADC reg_mcasp_adc_current, reg_mcasp_adc_current, reg_mcasp_dac_current MOV reg_frame_current, 0 WRITE_LOOP: - // Write 8 channels to DAC from successive values in memory - // At the same time, read 8 channels from ADC + // Write N channels to DAC from successive values in memory + // At the same time, read N channels from ADC // Unrolled by a factor of 2 to get high and low words MOV r1, 0 ADC_DAC_LOOP: @@ -544,7 +568,8 @@ // On even iterations, load two more samples and choose the first one // On odd iterations, transmit the second of the samples already loaded - QBBS MCASP_DAC_HIGH_WORD, r1, 1 + // QBBS MCASP_DAC_HIGH_WORD, r1, 1 + QBBS MCASP_DAC_HIGH_WORD, reg_flags, FLAG_BIT_MCASP_HWORD MCASP_DAC_LOW_WORD: // Load next 2 Audio DAC samples and store zero in their place LBCO reg_mcasp_dac_data, C_MCASP_MEM, reg_mcasp_dac_current, 4 @@ -561,8 +586,7 @@ // Take the high word of the previously loaded data LSR r7, reg_mcasp_dac_data, 16 - // Two audio frames per SPI frame = 4 audio samples per SPI frame - // Therefore every 2 channels we send one audio sample; this loop already + // Every 2 channels we send one audio sample; this loop already // sends exactly two SPI channels. // Wait for McASP XSTAT[XDATA] to set indicating we can write more data MCASP_WAIT_XSTAT: @@ -573,7 +597,8 @@ // Same idea with ADC: even iterations, load the sample into the low word, odd // iterations, load the sample into the high word and store - QBBS MCASP_ADC_HIGH_WORD, r1, 1 + // QBBS MCASP_ADC_HIGH_WORD, r1, 1 + QBBS MCASP_ADC_HIGH_WORD, reg_flags, FLAG_BIT_MCASP_HWORD MCASP_ADC_LOW_WORD: // Start ADC data at 0 LDI reg_mcasp_adc_data, 0 @@ -618,12 +643,13 @@ // Read ADC channels: result is always 2 commands behind // Start by reading channel 2 (result is channel 0) and go - // to 10, but masking the channel number to be between 0 and 7 + // to N+2, but masking the channel number to be between 0 and N-1 LDI reg_adc_data, 0 + ADD r8, r1, 2 + SUB r7, reg_num_channels, 1 + AND r8, r8, r7 + LSL r8, r8, AD7699_CHANNEL_OFFSET MOV r7, AD7699_CFG_MASK - ADD r8, r1, 2 - AND r8, r8, 7 - LSL r8, r8, AD7699_CHANNEL_OFFSET OR r7, r7, r8 ADC_WRITE r7, r7 @@ -645,11 +671,13 @@ // Read ADC channels: result is always 2 commands behind // Start by reading channel 2 (result is channel 0) and go - // to 10, but masking the channel number to be between 0 and 7 + // to N+2, but masking the channel number to be between 0 and N-1 + LDI reg_adc_data, 0 + ADD r8, r1, 2 + SUB r7, reg_num_channels, 1 + AND r8, r8, r7 + LSL r8, r8, AD7699_CHANNEL_OFFSET MOV r7, AD7699_CFG_MASK - ADD r8, r1, 2 - AND r8, r8, 7 - LSL r8, r8, AD7699_CHANNEL_OFFSET OR r7, r7, r8 ADC_WRITE r7, r7 @@ -661,16 +689,26 @@ SBCO reg_adc_data, C_ADC_DAC_MEM, reg_adc_current, 4 ADD reg_adc_current, reg_adc_current, 4 - // Repeat 4 times (2 samples per loop, r1 += 1 already happened) + // Toggle the high/low word for McASP control (since we send one word out of + // 32 bits for each pair of SPI channels) + XOR reg_flags, reg_flags, (1 << FLAG_BIT_MCASP_HWORD) + + // Repeat 4 times for 8 channels (2 samples per loop, r1 += 1 already happened) + // For 4 or 2 channels, repeat 2 or 1 times, according to flags ADD r1, r1, 1 - QBNE ADC_DAC_LOOP, r1, 8 + QBNE ADC_DAC_LOOP, r1, reg_num_channels QBA ADC_DAC_LOOP_DONE - + SPI_SKIP_WRITE: // We get here only if the SPI ADC and DAC are disabled // Just keep the loop going for McASP + + // Toggle the high/low word for McASP control (since we send one word out of + // 32 bits for each pair of SPI channels) + XOR reg_flags, reg_flags, (1 << FLAG_BIT_MCASP_HWORD) + ADD r1, r1, 2 - QBNE ADC_DAC_LOOP, r1, 8 + QBNE ADC_DAC_LOOP, r1, reg_num_channels ADC_DAC_LOOP_DONE: // Increment number of frames, see if we have more to write