From edede9d517c9a52907fa90c07a931eaed212525e Mon Sep 17 00:00:00 2001 From: cnlohr Date: Tue, 9 Jul 2024 02:17:18 -0700 Subject: [PATCH] Lots of general updates. closing in on a solution --- ch32v/ch32v003fun | 2 +- ch32v/ch32v203-goertzel/adcgoertzel.c | 98 +++++++++++++------- ch32v/ch32v203-goertzel/usb_config.h | 2 +- ch32v/lib/calculator.html | 19 +++- ch32v/lib/webhidcontrol.js | 126 +++++++++++++++++++++----- 5 files changed, 184 insertions(+), 63 deletions(-) diff --git a/ch32v/ch32v003fun b/ch32v/ch32v003fun index 7bf80c9..44959b2 160000 --- a/ch32v/ch32v003fun +++ b/ch32v/ch32v003fun @@ -1 +1 @@ -Subproject commit 7bf80c92ccc38aa4b25871739618309e141e77f1 +Subproject commit 44959b2ca16e5eb87a041afd89ac2ecccff35e15 diff --git a/ch32v/ch32v203-goertzel/adcgoertzel.c b/ch32v/ch32v203-goertzel/adcgoertzel.c index c28db87..efac46c 100644 --- a/ch32v/ch32v203-goertzel/adcgoertzel.c +++ b/ch32v/ch32v203-goertzel/adcgoertzel.c @@ -74,7 +74,7 @@ SOFTWARE. #include "./usb_config.h" #include "../ch32v003fun/examples_v20x/otg_device/otgusb.h" - +// Bigger buffer decreases chance of fall-through, but increases the size of each operation. #define ADC_BUFFSIZE 512 volatile uint16_t adc_buffer[ADC_BUFFSIZE]; @@ -141,8 +141,8 @@ const int32_t g_goertzel_coefficient_s = 2129111628; int intensity_average = 1; #define LOG_GOERTZEL_LIST 512 -int16_t qibaselogs[LOG_GOERTZEL_LIST*2]; -int qibaselogs_head; +int32_t qibaselogs[LOG_GOERTZEL_LIST]; +volatile int qibaselogs_head; void SetupADC() { @@ -270,9 +270,14 @@ uint32_t g_goertzel_outs; int32_t g_goertzel, g_goertzelp, g_goertzelp2; int32_t g_goertzelp_store, g_goertzelp2_store; +int32_t g_laststart = 0; +int32_t g_lastper; +int32_t g_lastlen; + void DMA1_Channel1_IRQHandler( void ) __attribute__((interrupt)); void DMA1_Channel1_IRQHandler( void ) { + int32_t start = SysTick->CNT; #ifdef PROFILING_PIN funDigitalWrite( PROFILING_PIN, 1 ); #endif @@ -360,26 +365,20 @@ void DMA1_Channel1_IRQHandler( void ) if( adc_tail == adc_buffer_top ) adc_tail = adc_buffer; if( goertzel_samples == g_goertzel_buffer ) { +#ifdef PROFILING_PIN + funDigitalWrite( PROFILING_PIN, 0 ); +#endif + g_goertzelp_store = goertzel - (g_goertzel_omega_per_sample>>(29-16)); g_goertzelp2_store = goertzelp; -// gertzellogs[gertzellogs_head++] = g_goertzelp_store; -// gertzellogs[gertzellogs_head++] = g_goertzelp2_store; -// gertzellogs_head = gertzellogs_head & ((LOG_GOERTZEL_LIST*2)-1); -// -// int32_t zp = gertzellogs[glread++]; -// int32_t zp2 = gertzellogs[glread++]; -// int32_t rr = (((int64_t)(g_goertzel_coefficient ) * (int64_t)zp<<1)>>32) - (zp2); -// int32_t ri = (((int64_t)(g_goertzel_coefficient_s) * (int64_t)zp<<1)>>32); - int32_t zp = g_goertzelp_store; int32_t zp2 = g_goertzelp2_store; int32_t rr = (((int64_t)(g_goertzel_coefficient ) * (int64_t)zp<<1)>>32) - (zp2); int32_t ri = (((int64_t)(g_goertzel_coefficient_s) * (int64_t)zp<<1)>>32); - qibaselogs[qibaselogs_head++] = rr; - qibaselogs[qibaselogs_head++] = ri; - qibaselogs_head = qibaselogs_head & ((LOG_GOERTZEL_LIST*2)-1); + qibaselogs[qibaselogs_head] = ((uint16_t)rr) | (((uint16_t)ri)<<16); + qibaselogs_head = ( qibaselogs_head + 1 ) & ((LOG_GOERTZEL_LIST)-1); rr>>=2; ri>>=2; @@ -392,7 +391,6 @@ void DMA1_Channel1_IRQHandler( void ) intensity = 1; intensity = (intensity + s/intensity)/2; intensity = (intensity + s/intensity)/2; - intensity = (intensity + s/intensity)/2; intensity_average = intensity_average - (intensity_average>>10) + (intensity); @@ -407,6 +405,10 @@ void DMA1_Channel1_IRQHandler( void ) goertzel = g_goertzel_omega_per_sample>>(29-16); goertzelp = 0; goertzel_samples = 0; +#ifdef PROFILING_PIN + funDigitalWrite( PROFILING_PIN, 1 ); +#endif + } } @@ -421,6 +423,10 @@ void DMA1_Channel1_IRQHandler( void ) #ifdef PROFILING_PIN funDigitalWrite( PROFILING_PIN, 0 ); // For profiling #endif + int32_t end = SysTick->CNT; + g_lastper = start - g_laststart; + g_laststart = start; + g_lastlen = end - start; } static inline uint32_t gets2() @@ -449,16 +455,17 @@ void InnerLoop() // Only display half of the list so the other half could // be updated by the ISR. - int glread = qibaselogs_head+LOG_GOERTZEL_LIST*2/2; + int glread = qibaselogs_head; - int intensity = 0; - for( pxa = 0; pxa < LOG_GOERTZEL_LIST/2; pxa++ ) + for( pxa = 0; pxa < LOG_GOERTZEL_LIST; pxa++ ) { - glread = (glread)&(LOG_GOERTZEL_LIST*2-1); - int rr = qibaselogs[glread++]; - int ri = qibaselogs[glread++]; + uint32_t combiq = qibaselogs[glread]; + glread = ( glread + 1 ) & ( LOG_GOERTZEL_LIST -1 ); + + int16_t rr = combiq & 0xffff; + int16_t ri = combiq >> 16; rr = rr * 512 / (intensity_average>>4); ri = ri * 512 / (intensity_average>>4); @@ -477,10 +484,9 @@ void InnerLoop() } #ifdef ENABLE_OLED - char cts[32]; - snprintf( cts, 32, "%d", intensity_average ); - - ssd1306_drawstr( 0, 0, cts, 1 ); + //char cts[32]; + //snprintf( cts, 32, "%d", intensity_average ); + //ssd1306_drawstr( 0, 0, cts, 1 ); ssd1306_refresh(); //static int ik = 0; @@ -599,21 +605,19 @@ int main() -uint8_t scratchpad[256]; +uint8_t scratchpad[512]; int g_isConfigurePacket = 0; int HandleHidUserSetReportSetup( struct _USBState * ctx, tusb_control_request_t * req ) { int id = req->wValue & 0xff; g_isConfigurePacket = 0; - if( id == 0xaa && req->wLength < sizeof( scratchpad ) ) + if( id == 0xaa && req->wLength <= sizeof( scratchpad ) ) { - // memset( scratchpad, 0x55, sizeof(scratchpad) ); - //printf( "SET REPORT! %d [%02x]\n", req->wLength, scratchpad[200] ); ctx->pCtrlPayloadPtr = scratchpad; return req->wLength; } - else if( id == 0xac && req->wLength < sizeof( scratchpad ) ) + else if( id == 0xac && req->wLength <= sizeof( scratchpad ) ) { g_isConfigurePacket = 1; ctx->pCtrlPayloadPtr = scratchpad; @@ -627,10 +631,40 @@ int HandleHidUserGetReportSetup( struct _USBState * ctx, tusb_control_request_t int id = req->wValue & 0xff; if( id == 0xaa ) { - //printf( "GET REPORT! %d\n", req->wLength ); ctx->pCtrlPayloadPtr = scratchpad; return 255; } + else if( id == 0xac ) + { + g_isConfigurePacket = 1; + ctx->pCtrlPayloadPtr = scratchpad; + return 63; + } + else if( id == 0xad ) + { + static int last_baselog; + + int samps_to_send = (qibaselogs_head - last_baselog + LOG_GOERTZEL_LIST * 2 - 1) & (LOG_GOERTZEL_LIST-1); + if( samps_to_send > 120 ) samps_to_send = 120; + + ((uint32_t*)scratchpad)[0] = (intensity_average<<8) | samps_to_send; + ((uint32_t*)scratchpad)[1] = (g_lastper<<16) | g_lastlen; + ((uint32_t*)scratchpad)[2] = 0; //Reserved. + + int i; + for( i = 3; i < samps_to_send + 3; i++ ) + { + last_baselog = (last_baselog+1)&(LOG_GOERTZEL_LIST-1); + ((uint32_t*)(scratchpad))[i] = ((int32_t*)qibaselogs)[last_baselog]; + } + + for( ; i < 128; i++ ) + ((uint32_t*)(scratchpad))[i] = 0; + + + ctx->pCtrlPayloadPtr = scratchpad; + return 510; + } return 0; } diff --git a/ch32v/ch32v203-goertzel/usb_config.h b/ch32v/ch32v203-goertzel/usb_config.h index d28e0e4..5ca870c 100644 --- a/ch32v/ch32v203-goertzel/usb_config.h +++ b/ch32v/ch32v203-goertzel/usb_config.h @@ -50,7 +50,7 @@ static const uint8_t HIDAPIRepDesc[ ] = HID_REPORT_ID ( 0xac ) HID_USAGE ( 0x01 ), HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) , - HID_REPORT_COUNT ( 254 ), // For receiving IQ data on host. + HID_REPORT_COUNT_N ( 510,2 ), // For receiving IQ data on host. HID_REPORT_ID ( 0xad ) HID_USAGE ( 0x01 ), HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) , diff --git a/ch32v/lib/calculator.html b/ch32v/lib/calculator.html index 7d77641..2c312ed 100644 --- a/ch32v/lib/calculator.html +++ b/ch32v/lib/calculator.html @@ -198,23 +198,34 @@ function onLoad() + + +

Tool for computing tuning to specific frequencies by use of direct ADC reading at specific timer-controlled rate to "tune" to specific frequencies either by quadrature or differential.

- + + + + +
+ +
- +
- - + +
Crystal MHz
Target MHz
Quanta (Goertzel's Algorithm Only)
Quanta Search Range (Goertzel's Algorithm Only)
Quanta (Goertzel's Only)
Quanta Search Range (Goertzel's Only)
Table TypeQuadratureGoertzels
+ Live Control:
+
diff --git a/ch32v/lib/webhidcontrol.js b/ch32v/lib/webhidcontrol.js index ca05975..5427d47 100644 --- a/ch32v/lib/webhidcontrol.js +++ b/ch32v/lib/webhidcontrol.js @@ -3,6 +3,23 @@ const filter = { vendorId : 0x1209, productId : 0xd035 }; let dev = null; let loopAbort = false; +const IQHistoryLen = 2048; +var IQHistoryArray = new Uint32Array(IQHistoryLen); +var IQHistoryHead = 0|0; +var lastIntensity = 1.0; +var lastNumQ = 0; +var lastTotalTime = 1; +var lastTimeUsed = 1; + +var graphIsClicked = false; + +function graphClick( e ) +{ + if( e.type == "mousedown" ) graphIsClicked = true; + if( e.type == "mouseup" ) graphIsClicked = false; + return true; +} + function setStatus( msg ) { document.getElementById( "STATUS" ).innerHTML = msg; @@ -43,6 +60,10 @@ async function closeDeviceTool() function onLoadWebHidControl() { + liveGraph = document.getElementById( "LiveGraph" ); + liveGraph.addEventListener( "mousedown", graphClick ); + liveGraph.addEventListener( "mouseup", graphClick ); + setTimeout( sendLoop, 1 ); if( !navigator.hid ) @@ -118,25 +139,26 @@ async function sendLoopError( e ) async function sendLoop() { const sleep = ms => new Promise(r => setTimeout(r, ms)); - var arraySend = new Uint8Array(255); + //var arraySend = new Uint8Array(255); var frameNo = 0|0; var lastTime = performance.now(); let goodCount = 0; let badCount = 0; let kBsecAvg = 0; let xActionSecAvg = 0; + while( true ) { if( dev && !loopAbort ) { - var i = 0|0; - for( var i = 0|0; i < 255|0; i++ ) - arraySend[i] = (Math.random()*256)|0; + //var i = 0|0; + //for( var i = 0|0; i < 255|0; i++ ) + // arraySend[i] = (Math.random()*256)|0; - sendReport = dev.sendFeatureReport( 0xAA, arraySend ).catch( sendLoopError ); - if( !sendReport ) sendLoopError( "error creating sendFeatureReport" ); + //sendReport = dev.sendFeatureReport( 0xAA, arraySend ).catch( sendLoopError ); + //if( !sendReport ) sendLoopError( "error creating sendFeatureReport" ); - receiveReport = dev.receiveFeatureReport( 0xAA ).catch( sendLoopError ); + receiveReport = dev.receiveFeatureReport( 0xAD ).catch( sendLoopError ); if( !receiveReport ) sendLoopError( "error creating receiveReport" ); frameNo++; @@ -157,6 +179,63 @@ async function sendLoop() "Good Count: " + goodCount + "
Bad Count: " + badCount; lastTime = thisTime; } + else if( frameNo % updateStatsPerfPer == 2 ) + { + const ctx = liveGraph.getContext("2d"); + + if( !graphIsClicked ) + { + let liveGraphContainer = document.getElementById( "LiveGraphContainer" ); + liveGraph.width = liveGraphContainer.clientWidth; + liveGraph.style.position = 'absolute'; + + ctx.clearRect(0, 0, liveGraph.width, liveGraph.height); + + + var filledness = lastNumQ * 198 / 120; + ctx.fillStyle = "rgb( 240 240 240 )"; + ctx.fillRect( 2, 2 + 198 - filledness, 18, filledness - 2); + + filledness = ( lastTimeUsed * 1.0 / lastTotalTime ) * 198; + ctx.fillStyle = "rgb( 240 240 240 )"; + ctx.fillRect( 26, 2 + 198 - filledness, 18, filledness - 2 ); + + ctx.fillStyle = `rgb( 255 255 255 )`; + + let mulcoeff = 10000.0 / lastIntensity; + var lot = 1.2; + var x = 253+IQHistoryLen; + for( var i = (IQHistoryHead+1) & (IQHistoryLen-1); i != IQHistoryHead|0; i = (i + 1) & (IQHistoryLen-1) ) + { + let v = IQHistoryArray[i]; + let real = (v >> 16); + let imag = (v & 0xffff); + if( real > 32767 ) real -= 65536; + if( imag > 32767 ) imag -= 65536; + let power = Math.sqrt( real * real + imag * imag ) * mulcoeff; + let phase = Math.atan2( real, imag ) * 0.159155078*0.5; + real = real * mulcoeff + 100; + imag = imag * mulcoeff + 100; + if( real < 0 ) real = 0; if( real > 255 ) real = 255; + if( imag < 0 ) imag = 0; if( imag > 255 ) imag = 255; + ctx.fillRect(x,power+10,2,2); + ctx.fillRect(x,phase*140+150,2,2); + x--; + ctx.globalAlpha = (lot>0)?lot:0; + ctx.fillRect(real+50,imag,2,2); + ctx.globalAlpha = 1.0; + lot -= 0.0015; + } + + ctx.strokeStyle = "rgb( 128 128 128 )"; + ctx.fillStyle = "rgb( 128 0 0 )"; + ctx.strokeRect( 1, 1, 20, 198 ); + ctx.strokeRect( 25, 1, 20, 198 ); + ctx.strokeRect( 49, 1, 200, 198 ); + ctx.strokeRect( 253, 1, liveGraph.width, 198 ); + } + } + if( sendReport ) { @@ -164,28 +243,25 @@ async function sendLoop() } if( receiveReport ) { - // Validate Data. let receiveData = await receiveReport; if( receiveData && receiveData.buffer ) { - let data = new Uint8Array( receiveData.buffer ); - - // Tricky: Data goes: - // reportID -> Payload... - let failed = false; - for( var i = 0|0; i < 254|0; i++ ) + let data = new Uint32Array( receiveData.buffer.slice( 0, 508 ) ); + let intensity = data[0]>>8; + let numq = data[0] & 0xff; + let time_total = data[1]>>16; + let time_used = data[1]&0xffff; + //console.log( data ); + for( var i = 0|0; i < numq; i++ ) { - if( data[i+1] != arraySend[i] ) - { - console.log( "Disagreement at index " + i ); - console.log( data ); - console.log( arraySend ); - badCount++; - failed = true; - break; - } + IQHistoryArray[IQHistoryHead++] = data[i+3]; + if( IQHistoryHead == IQHistoryLen ) IQHistoryHead = 0; } - if( !failed ) goodCount++; + lastIntensity = intensity; + lastNumQ = numq; + lastTotalTime = time_total; + lastTimeUsed = time_used; + goodCount++; } else { @@ -217,7 +293,7 @@ async function sendLoop() await sleep(100); if( dev ) { - break; + //break; } } }