mirror of
https://github.com/cnlohr/lolra.git
synced 2026-06-22 02:39:27 +00:00
Compare commits
30 Commits
ae273dbcce
...
84b7b478c0
| Author | SHA1 | Date | |
|---|---|---|---|
| 84b7b478c0 | |||
| 64c8253055 | |||
| 7f3ebcc732 | |||
| e9f97f7757 | |||
| 8cf6fa53a9 | |||
| 0379e7cb30 | |||
| 5028657d02 | |||
| 21e6aab95c | |||
| 6c7adbdcc2 | |||
| edede9d517 | |||
| e0d41ca056 | |||
| d206128b38 | |||
| 0a6e76034f | |||
| 4fe9638062 | |||
| 0f71fdafe0 | |||
| a8ab17013f | |||
| 41ec379ad6 | |||
| e0db86d513 | |||
| 26d8facf2c | |||
| 25dc0234c0 | |||
| 2b75c51cd9 | |||
| 9f0cc2c50d | |||
| 5067970ec3 | |||
| 64335ae653 | |||
| 7052e8c111 | |||
| 70caf8b76a | |||
| 17b7d3fdcb | |||
| 9bbca912a0 | |||
| 696701ffb7 | |||
| f31cb4595d |
@@ -91,17 +91,13 @@ SOFTWARE.
|
|||||||
Calculated to use the 19.75th harmonic @ 27.08571429MHz, but ideal found at 27.08643MHz
|
Calculated to use the 19.75th harmonic @ 27.08571429MHz, but ideal found at 27.08643MHz
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define Q 1800
|
#define Q 1000
|
||||||
|
|
||||||
|
#define PWM_PERIOD (28-1) //For 27.000500MHz
|
||||||
// For Quadrature - use 30
|
|
||||||
// For nonquadrature use 40.
|
|
||||||
|
|
||||||
#define PWM_PERIOD (40-1) //For 27MHz
|
|
||||||
//#define QUADRATURE
|
//#define QUADRATURE
|
||||||
//#define TIGHT_OUT
|
//#define TIGHT_OUT
|
||||||
//#define DUMPBUFF
|
//#define DUMPBUFF
|
||||||
#define PWM_OUTPUT 1
|
#define PWM_OUTPUT 7
|
||||||
|
|
||||||
//#define PWM_PERIOD (32-1)
|
//#define PWM_PERIOD (32-1)
|
||||||
//#define QUADRATURE
|
//#define QUADRATURE
|
||||||
@@ -124,12 +120,13 @@ void SetupADC()
|
|||||||
// Set up single conversion on chl 7
|
// Set up single conversion on chl 7
|
||||||
ADC1->RSQR1 = 0;
|
ADC1->RSQR1 = 0;
|
||||||
ADC1->RSQR2 = 0;
|
ADC1->RSQR2 = 0;
|
||||||
ADC1->RSQR3 = 7; // 0-9 for 8 ext inputs and two internals
|
ADC1->RSQR3 = 6; // 0-9 for 8 ext inputs and two internals /// 7 or 6 means one of the ADC inputs.
|
||||||
|
|
||||||
// Not using injection group.
|
// Not using injection group.
|
||||||
|
|
||||||
// PD4 is analog input chl 7
|
// PD4 is analog input chl 7 + 6
|
||||||
GPIOD->CFGLR &= ~(0xf<<(4*4)); // CNF = 00: Analog, MODE = 00: Input
|
GPIOD->CFGLR &= ~(0xf<<(4*4)); // CNF = 00: Analog, MODE = 00: Input
|
||||||
|
GPIOD->CFGLR &= ~(0xf<<(4*6)); // CNF = 00: Analog, MODE = 00: Input
|
||||||
|
|
||||||
// Sampling time for channels. Careful: This has PID tuning implications.
|
// Sampling time for channels. Careful: This has PID tuning implications.
|
||||||
// Note that with 3 and 3,the full loop (and injection) runs at 138kHz.
|
// Note that with 3 and 3,the full loop (and injection) runs at 138kHz.
|
||||||
@@ -281,14 +278,16 @@ void InnerLoop()
|
|||||||
int s = 1<<( ( 32 - __builtin_clz(is) )/2);
|
int s = 1<<( ( 32 - __builtin_clz(is) )/2);
|
||||||
s = (s + is/s)/2;
|
s = (s + is/s)/2;
|
||||||
|
|
||||||
|
|
||||||
#ifdef TIGHT_OUT
|
#ifdef TIGHT_OUT
|
||||||
printf( "%d\n", s );
|
printf( "%d\n", is );
|
||||||
#elif defined( PWM_OUTPUT )
|
#elif defined( PWM_OUTPUT )
|
||||||
int tv = (s>>PWM_OUTPUT) + (PWM_PERIOD/2);
|
|
||||||
|
|
||||||
|
int tv = (i>>PWM_OUTPUT) + (PWM_PERIOD/2);
|
||||||
if( tv < 0 ) tv = 0;
|
if( tv < 0 ) tv = 0;
|
||||||
if( tv >= PWM_PERIOD ) tv = PWM_PERIOD-1;
|
if( tv >= PWM_PERIOD ) tv = PWM_PERIOD-1;
|
||||||
TIM1->CH3CVR = tv;
|
|
||||||
|
TIM1->CH4CVR = tv;
|
||||||
#else
|
#else
|
||||||
|
|
||||||
printf( "%8d I:%7d Q:%7d [%d %d %d %d] / %d\n",s, i ,q, adc_buffer[0], adc_buffer[1], adc_buffer[2], adc_buffer[3], (int)(SysTick->CNT - tstart) );
|
printf( "%8d I:%7d Q:%7d [%d %d %d %d] / %d\n",s, i ,q, adc_buffer[0], adc_buffer[1], adc_buffer[2], adc_buffer[3], (int)(SysTick->CNT - tstart) );
|
||||||
|
|||||||
@@ -1,92 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<HTML>
|
|
||||||
<HEAD>
|
|
||||||
<SCRIPT>
|
|
||||||
|
|
||||||
|
|
||||||
function DrawSpan( colspan, freq, target, docolor )
|
|
||||||
{
|
|
||||||
var fdist = Math.abs( freq - target );
|
|
||||||
fdist = Math.pow( fdist, 0.5 ) * 500;
|
|
||||||
// if( fdist > 255 ) fdist = 255;
|
|
||||||
let ret = "<TD COLSPAN=" + colspan + ' ';
|
|
||||||
if( docolor ) ret += 'STYLE="background-color:rgb(' + fdist + ',' + (511-fdist) + ',0)";';
|
|
||||||
ret += '>' + freq.toFixed(6) + "</TD>";
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
function computeTable()
|
|
||||||
{
|
|
||||||
const max_harmonics = 28|0;
|
|
||||||
const min_harmonics = 1|0;
|
|
||||||
let xtal = Number(document.getElementById("crystalmhz").value );
|
|
||||||
let target = Number(document.getElementById("targetmhz").value );
|
|
||||||
let contents = "<TABLE BORDER=1>";
|
|
||||||
|
|
||||||
contents += '<TR><TH>d\\h</div></TH>';
|
|
||||||
for( let h = 0|min_harmonics; h <= max_harmonics; h++ )
|
|
||||||
{
|
|
||||||
contents += "<TH COLSPAN=2>" + h + "</TH>";
|
|
||||||
}
|
|
||||||
contents += "</TR>";
|
|
||||||
|
|
||||||
|
|
||||||
for( let n = 0|28; n <= 66; n++ )
|
|
||||||
{
|
|
||||||
for( let mode = 0; mode < 4; mode++ )
|
|
||||||
{
|
|
||||||
contents += "<TR>";
|
|
||||||
if( mode == 0 )
|
|
||||||
contents += "<TD ROWSPAN=4>" + n + "</TD>";
|
|
||||||
for( let h = 0|min_harmonics; h <= max_harmonics; h++ )
|
|
||||||
{
|
|
||||||
let freq = ( xtal / n );
|
|
||||||
if( mode == 0 )
|
|
||||||
contents += DrawSpan( 2, freq * h, target, false );
|
|
||||||
else if( mode == 1 )
|
|
||||||
contents += DrawSpan( 2, freq * (h-.25), target, true );
|
|
||||||
else if( mode == 2 )
|
|
||||||
contents += DrawSpan( 2, freq * (h+.25), target, true );
|
|
||||||
else if( mode == 3 )
|
|
||||||
contents += DrawSpan( 2, freq * (h+0.5), target, true );
|
|
||||||
|
|
||||||
}
|
|
||||||
contents += "</TD>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
contents += "</TABLE>";
|
|
||||||
document.getElementById( "TABLE" ).innerHTML = contents;
|
|
||||||
}
|
|
||||||
|
|
||||||
</SCRIPT>
|
|
||||||
</HEAD>
|
|
||||||
<BODY>
|
|
||||||
|
|
||||||
<p>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.</p>
|
|
||||||
<TABLE>
|
|
||||||
<TR><TD>Crystal MHz</TD><TD><INPUT ID=crystalmhz VALUE=48></TD></TR>
|
|
||||||
<TR><TD>Target MHz</TD><TD><INPUT ID=targetmhz VALUE=27.1></TD></TR>
|
|
||||||
<TR><TD COLSPAN=2><INPUT TYPE=SUBMIT ONCLICK="computeTable()"></TD></TR>
|
|
||||||
</TABLE>
|
|
||||||
|
|
||||||
<TABLE>
|
|
||||||
<TR><TD>Quadrature:</TD></TR>
|
|
||||||
<TR><TD>I = + + - -</TD></TR>
|
|
||||||
<TR><TD>Q = + - - +</TD></TR>
|
|
||||||
<TR><TD>Differntial:</TD></TR>
|
|
||||||
<TR><TD>V = + - + -</TD></TR>
|
|
||||||
<TR><TD>You choose the mode you operate in, either Quadrature or differential</TD></TR>
|
|
||||||
</TABLE>
|
|
||||||
|
|
||||||
<p> Table shows: </P>
|
|
||||||
<TABLE BORDER=1>
|
|
||||||
<TR><TD>Sample Frequency Harmonic</TD></TR>
|
|
||||||
<TR><TD>Lower Quadrature Frequency</TD></TR>
|
|
||||||
<TR><TD>Upper Quadrature Frequency</TD></TR>
|
|
||||||
<TR><TD>Differential Frequency</TD></TR>
|
|
||||||
</TABLE>
|
|
||||||
|
|
||||||
<DIV ID=TABLE></DIV>
|
|
||||||
|
|
||||||
</BODY>
|
|
||||||
</HTML>
|
|
||||||
@@ -0,0 +1,551 @@
|
|||||||
|
/**
|
||||||
|
|
||||||
|
MIT-like-non-ai-license
|
||||||
|
|
||||||
|
Copyright (c) 2024 Charles Lohr "CNLohr"
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the two following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
In addition the following restrictions apply:
|
||||||
|
|
||||||
|
1. The Software and any modifications made to it may not be used for the
|
||||||
|
purpose of training or improving machine learning algorithms, including but not
|
||||||
|
limited to artificial intelligence, natural language processing, or data
|
||||||
|
mining. This condition applies to any derivatives, modifications, or updates
|
||||||
|
based on the Software code. Any usage of the Software in an AI-training dataset
|
||||||
|
is considered a breach of this License.
|
||||||
|
|
||||||
|
2. The Software may not be included in any dataset used for training or
|
||||||
|
improving machine learning algorithms, including but not limited to artificial
|
||||||
|
intelligence, natural language processing, or data mining.
|
||||||
|
|
||||||
|
|
||||||
|
3. Any person or organization found to be in violation of these restrictions
|
||||||
|
will be subject to legal action and may be held liable for any damages
|
||||||
|
resulting from such use.
|
||||||
|
|
||||||
|
If any term is unenforcable, other terms remain in-force.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
|
||||||
|
// NOT LORA!!!
|
||||||
|
|
||||||
|
// Transmit a sweep on 97.7MHz
|
||||||
|
#define FM_TRANSMITTER_SWEEP
|
||||||
|
|
||||||
|
// Nothing = Transmit a 315MHz signal.
|
||||||
|
|
||||||
|
// XXX WARNING: Something is wrong with this -
|
||||||
|
// The output isn't perfectly time aligned
|
||||||
|
// And as such there are extra images in weird places.
|
||||||
|
// TODO: Investigate the DMA+SPI Port jankyness.
|
||||||
|
#include "ch32v003fun.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "LoRa-SDR-Code.h"
|
||||||
|
|
||||||
|
#ifdef LORAWAN
|
||||||
|
#include "lorawan_simple.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define DMA_SIZE_WORDS 128
|
||||||
|
|
||||||
|
#define SENDBUFF_WORDS (DMA_SIZE_WORDS*2)
|
||||||
|
uint8_t sendbuff[SENDBUFF_WORDS];
|
||||||
|
|
||||||
|
// Bits are shifted out MSBit first, then to LSBit
|
||||||
|
|
||||||
|
|
||||||
|
#define MAX_BYTES 25
|
||||||
|
#define MAX_SYMBOLS (MAX_BYTES*2+16)
|
||||||
|
|
||||||
|
// Our table is bespoke for the specific SF.
|
||||||
|
#define CHIPSSPREAD CHIRPLENGTH_WORDS// QUARTER_CHIRP_LENGTH_WORDS (TODO: Use the quater value elsewhere in the code)
|
||||||
|
#define MARK_FROM_SF0 (1<<SF_NUMBER) // SF7
|
||||||
|
|
||||||
|
#define PREAMBLE_CHIRPS 10
|
||||||
|
#define CODEWORD_LENGTH 2
|
||||||
|
|
||||||
|
uint32_t quadsetcount;
|
||||||
|
int16_t quadsets[MAX_SYMBOLS*4+PREAMBLE_CHIRPS*4+9+CODEWORD_LENGTH*4];
|
||||||
|
int runningcount_bits = 0;
|
||||||
|
volatile int fxcycle;
|
||||||
|
volatile int quadsetplace = -1;
|
||||||
|
volatile uint32_t temp;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
|
int16_t * AddChirp( int16_t * qso, int offset, int verneer )
|
||||||
|
{
|
||||||
|
offset = offset * CHIPSSPREAD / (MARK_FROM_SF0);
|
||||||
|
offset += verneer;
|
||||||
|
*(qso++) = (CHIPSSPREAD * 0 / 4 + offset + CHIPSSPREAD ) % CHIPSSPREAD;
|
||||||
|
*(qso++) = (CHIPSSPREAD * 1 / 4 + offset + CHIPSSPREAD ) % CHIPSSPREAD;
|
||||||
|
*(qso++) = (CHIPSSPREAD * 2 / 4 + offset + CHIPSSPREAD ) % CHIPSSPREAD;
|
||||||
|
*(qso++) = (CHIPSSPREAD * 3 / 4 + offset + CHIPSSPREAD ) % CHIPSSPREAD;
|
||||||
|
return qso;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This IRQ is called periodically to fill the output buffer that is shifted to SPI
|
||||||
|
void DMA1_Channel3_IRQHandler( void ) __attribute__((interrupt)) __attribute__((section(".srodata")));
|
||||||
|
void DMA1_Channel3_IRQHandler( void )
|
||||||
|
{
|
||||||
|
//GPIOD->BSHR = 1; // Turn on GPIOD0 for profiling
|
||||||
|
|
||||||
|
// Backup flags.
|
||||||
|
volatile int intfr = DMA1->INTFR;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// Clear all possible flags.
|
||||||
|
DMA1->INTFCR = DMA1_IT_GL3;
|
||||||
|
|
||||||
|
|
||||||
|
//int place = DMA1_Channel3->CNTR;
|
||||||
|
// CNTR says that there are THIS MANY bytes left in the transfer.
|
||||||
|
// So a high CNTR value indicates a very early place in the buffer.
|
||||||
|
|
||||||
|
uint16_t * sb = 0;
|
||||||
|
|
||||||
|
temp++;
|
||||||
|
|
||||||
|
if( intfr & DMA1_IT_HT3 )
|
||||||
|
{
|
||||||
|
sb = sendbuff;
|
||||||
|
}
|
||||||
|
else if( intfr & DMA1_IT_TC3 )
|
||||||
|
{
|
||||||
|
sb = sendbuff + SENDBUFF_WORDS/2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( sb )
|
||||||
|
{
|
||||||
|
if( quadsetplace < 0 )
|
||||||
|
{
|
||||||
|
// Abort Send
|
||||||
|
memset( sb, 0, SENDBUFF_WORDS*2/2 );
|
||||||
|
DMA1_Channel3->CFGR &= ~DMA_CFGR1_EN;
|
||||||
|
goto complete;
|
||||||
|
}
|
||||||
|
|
||||||
|
fxcycle += DMA_SIZE_WORDS;
|
||||||
|
if( fxcycle == NUM_DMAS_PER_QUARTER_CHIRP*DMA_SIZE_WORDS )
|
||||||
|
{
|
||||||
|
fxcycle = 0;
|
||||||
|
// Advance to next quarter word.
|
||||||
|
quadsetplace++;
|
||||||
|
|
||||||
|
if( quadsetplace > quadsetcount )
|
||||||
|
{
|
||||||
|
#ifdef TEST_TONE
|
||||||
|
quadsetplace = 0;
|
||||||
|
#else
|
||||||
|
quadsetplace = -1;
|
||||||
|
#endif
|
||||||
|
memset( sb, 0, SENDBUFF_WORDS*2/2 );
|
||||||
|
goto complete;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int symbol = quadsets[quadsetplace]; // Actually 0...CHIRPLENGTHWORDS
|
||||||
|
|
||||||
|
const uint16_t * tsb = 0;
|
||||||
|
|
||||||
|
// Select down- or up-chirp.
|
||||||
|
if( symbol < 0 )
|
||||||
|
{
|
||||||
|
int word = fxcycle - symbol - 1;
|
||||||
|
if( word >= CHIRPLENGTH_WORDS ) word -= CHIRPLENGTH_WORDS;
|
||||||
|
word++;
|
||||||
|
tsb = (&chirpbuff[word+REVERSE_START_OFFSET_BYTES/4]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int word = fxcycle + symbol;
|
||||||
|
if( word >= CHIRPLENGTH_WORDS ) word -= CHIRPLENGTH_WORDS;
|
||||||
|
tsb = (&chirpbuff[word]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// I tried using the DMA to do the copy - but that didn't work for some reason.
|
||||||
|
//while( DMA1_Channel5->CFGR & 1 );
|
||||||
|
// DMA1_Channel5->CNTR = DMA_SIZE_WORDS/2;
|
||||||
|
// DMA1_Channel5->MADDR = (uint32_t)tsb;
|
||||||
|
// DMA1_Channel5->PADDR = (uint32_t)sb;
|
||||||
|
// DMA1_Channel5->CFGR |= DMA_CFGR1_EN;
|
||||||
|
|
||||||
|
int cpy = DMA_SIZE_WORDS/2;
|
||||||
|
#ifdef TEST_TONE
|
||||||
|
// Test tone
|
||||||
|
memset( sb, 0xaa, cpy*2 );
|
||||||
|
#else
|
||||||
|
#if DMA_SIZE_WORDS_DIVISIBLE_BY_FOUR == 0
|
||||||
|
#error need divisibiltiy by 2.
|
||||||
|
#else
|
||||||
|
//while( cpy-- ) { *(sb++) = wordo; }
|
||||||
|
// Guarantee aligned access.
|
||||||
|
uint32_t * sbw = (uint32_t*)(((uint32_t)sb)&~3);
|
||||||
|
uint32_t * tsbw = (uint32_t*)(((uint32_t)tsb)&~3);
|
||||||
|
//while( cpy-- ) { *(sbw++) = *(tsbw++); }
|
||||||
|
|
||||||
|
// Align the data copy if needed
|
||||||
|
if( cpy & 1 )
|
||||||
|
{
|
||||||
|
*(sbw++) = *(tsbw++);
|
||||||
|
}
|
||||||
|
cpy /= 2; // Doubled up per loop
|
||||||
|
asm volatile("\
|
||||||
|
1:\
|
||||||
|
c.lw a3, 0(%[from])\n\
|
||||||
|
c.lw a4, 4(%[from])\n\
|
||||||
|
c.addi %[from], 8\n\
|
||||||
|
c.addi %[cpy], -1\n\
|
||||||
|
c.sw a3, 0(%[to])\n\
|
||||||
|
c.sw a4, 4(%[to])\n\
|
||||||
|
c.addi %[to], 8\n\
|
||||||
|
c.bnez %[cpy], 1b\n\
|
||||||
|
" : [cpy]"+r"(cpy), [from]"+r"(tsbw), [to]"+r"(sbw) : : "a3", "a4", "memory");
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
complete:
|
||||||
|
intfr = DMA1->INTFR;
|
||||||
|
} while( intfr );
|
||||||
|
|
||||||
|
//GPIOD->BSHR = 1<<16; // Turn off GPIOD0 for profiling
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void LoopFunction() __attribute__((section(".srodata")));
|
||||||
|
void LoopFunction()
|
||||||
|
{
|
||||||
|
uint8_t * start = (uint8_t*)DMA1_Channel5->MADDR;
|
||||||
|
uint8_t * end = (uint8_t*)((uint32_t)DMA1_Channel5->MADDR + SENDBUFF_WORDS);
|
||||||
|
uint8_t * here = start+ 8;
|
||||||
|
uint32_t targ = 2000;
|
||||||
|
uint32_t running = 0;
|
||||||
|
uint8_t * tail = end - DMA1_Channel5->CNTR;
|
||||||
|
uint32_t * cntr = DMA1_Channel5->CNTR;
|
||||||
|
uint32_t temp = 0;
|
||||||
|
uint32_t temp2 = 0;
|
||||||
|
|
||||||
|
asm volatile("\n\
|
||||||
|
li %[targ], 2000\n\
|
||||||
|
genloop:\n\
|
||||||
|
lw %[temp], 0(%[cntr])\n\
|
||||||
|
sub %[tail], %[end], %[temp]\n\
|
||||||
|
beq %[here], %[tail], genloop\n\
|
||||||
|
innerloop:\
|
||||||
|
li %[temp], 17\n\
|
||||||
|
blt %[running], %[targ], noskip\n\
|
||||||
|
li %[temp], 18\n\
|
||||||
|
sub %[running], %[running], %[targ]\n\
|
||||||
|
noskip:\n\
|
||||||
|
sb %[temp], 0(%[here])\n\
|
||||||
|
addi %[here], %[here], 1\n\
|
||||||
|
bne %[here], %[end], skipreset\n\
|
||||||
|
add %[here], x0, %[start]\n\
|
||||||
|
skipreset:\n\
|
||||||
|
bne %[here], %[tail], innerloop\n\
|
||||||
|
j genloop\n\
|
||||||
|
" : [here]"+r"(here) :
|
||||||
|
[start]"r"(start),
|
||||||
|
[end]"r"(end),
|
||||||
|
[targ]"r"(targ),
|
||||||
|
[running]"r"(running),
|
||||||
|
[tail]"r"(tail),
|
||||||
|
[cntr]"r"(cntr),
|
||||||
|
[temp]"r"(temp),
|
||||||
|
[temp2]"r"(temp2) );
|
||||||
|
|
||||||
|
/*
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
int targ_f = 2000; //(frameno & 511)*9 + 1700;
|
||||||
|
int run_f = 0;
|
||||||
|
uint8_t * tail = end - DMA1_Channel5->CNTR;
|
||||||
|
|
||||||
|
while( here != tail )
|
||||||
|
{
|
||||||
|
int setf = 17;
|
||||||
|
if( run_f > targ_f )
|
||||||
|
{
|
||||||
|
setf = 18;
|
||||||
|
run_f -= targ_f;
|
||||||
|
}
|
||||||
|
run_f += setf*32;
|
||||||
|
|
||||||
|
*here = setf;
|
||||||
|
|
||||||
|
here++;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
for( j = 0; j < sizeof( sendbuff ); j++ )
|
||||||
|
{
|
||||||
|
int setf = 10;
|
||||||
|
if( run_f > targ_f )
|
||||||
|
{
|
||||||
|
setf = 9;
|
||||||
|
run_f -= targ_f;
|
||||||
|
}
|
||||||
|
run_f += setf*32;
|
||||||
|
|
||||||
|
sendbuff[j] = setf;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
void LoopFunction2() __attribute__((aligned(256))) __attribute__((section(".srodata"))) __attribute__ ((noinline));
|
||||||
|
|
||||||
|
__attribute__((section(".sdata"))) __attribute__((aligned(256))) const uint32_t tablef[] = {
|
||||||
|
0x06060606,
|
||||||
|
0x06060607,
|
||||||
|
0x06070607,
|
||||||
|
0x07070706,
|
||||||
|
0x07070707,
|
||||||
|
0x07070708,
|
||||||
|
0x07080708,
|
||||||
|
0x08080807,
|
||||||
|
0x08080808,
|
||||||
|
0x08080809,
|
||||||
|
0x08090809,
|
||||||
|
0x09090908,
|
||||||
|
0x09090909,
|
||||||
|
0x0909090a,
|
||||||
|
0x090a090a,
|
||||||
|
0x0a0a0a09,
|
||||||
|
0x0a0a0a0a, // Below this line is unstable - i.e. sometimes there are missing DMA transfers.
|
||||||
|
0x0a0a0a0b,
|
||||||
|
0x0a0b0a0b,
|
||||||
|
0x0b0b0b0a,
|
||||||
|
0x0b0b0b0b,
|
||||||
|
0x0b0b0b0c,
|
||||||
|
0x0b0c0b0c,
|
||||||
|
0x0c0c0c0b,
|
||||||
|
0x0c0c0c0c,
|
||||||
|
0x0c0c0c0d,
|
||||||
|
0x0c0d0c0d,
|
||||||
|
0x0d0d0d0c,
|
||||||
|
0x0d0d0d0d,
|
||||||
|
0x0d0d0d0e,
|
||||||
|
0x0d0e0d0e,
|
||||||
|
0x0e0e0e0d,
|
||||||
|
0x0e0e0e0e,
|
||||||
|
0x0e0e0e0f,
|
||||||
|
0x0e0f0e0f,
|
||||||
|
0x0f0f0f0e,
|
||||||
|
0x0f0f0f0f,
|
||||||
|
0x0f0f0f10,
|
||||||
|
0x0f100f10,
|
||||||
|
0x1010100f,
|
||||||
|
0x10101010,
|
||||||
|
0x10101011,
|
||||||
|
0x10111011,
|
||||||
|
0x11111110,
|
||||||
|
};
|
||||||
|
|
||||||
|
void LoopFunction2()
|
||||||
|
{
|
||||||
|
|
||||||
|
uint32_t * start = (uint8_t*)DMA1_Channel5->MADDR;
|
||||||
|
uint32_t * end = (uint8_t*)((uint32_t)DMA1_Channel5->MADDR + SENDBUFF_WORDS);
|
||||||
|
uint32_t * here = start;
|
||||||
|
|
||||||
|
int run_f = 0;
|
||||||
|
|
||||||
|
volatile uint32_t * cntrptr = &DMA1_Channel5->CNTR;
|
||||||
|
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
//uint32_t * tail = 0xfffffffc & (uintptr_t)(((uint8_t*)end) - *cntrptr);
|
||||||
|
//if( tail == end ) tail--;
|
||||||
|
uint32_t * tail = ((SENDBUFF_WORDS-1) & (0xfffffffc)) & (uintptr_t)(((uint8_t*)start) + SENDBUFF_WORDS - *cntrptr);
|
||||||
|
|
||||||
|
while( here != tail )
|
||||||
|
{
|
||||||
|
#ifdef FM_TRANSMITTER_SWEEP
|
||||||
|
// 97.7MHz FM Station
|
||||||
|
int notein = (SysTick->CNT>>3)&0x1fff;
|
||||||
|
if( notein > 0xfff ) notein = 0x2000-notein;
|
||||||
|
uint32_t cp = (notein)+0x20000;
|
||||||
|
#else
|
||||||
|
// 315MHz
|
||||||
|
uint32_t cp = 0x1bfc3;
|
||||||
|
#endif
|
||||||
|
*(here++) = tablef[run_f>>12];
|
||||||
|
run_f &= (1<<12)-1;
|
||||||
|
run_f += cp;
|
||||||
|
if( here == end )
|
||||||
|
here = start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
SystemInit();
|
||||||
|
|
||||||
|
funGpioInitAll();
|
||||||
|
|
||||||
|
// Set a wait state (1 = normal <= 48MHz)
|
||||||
|
FLASH->ACTLR = 1;
|
||||||
|
|
||||||
|
// MCO for testing.
|
||||||
|
// funPinMode( PA8, GPIO_CFGLR_OUT_50Mhz_AF_PP ); RCC->CFGR0 |= RCC_CFGR0_MCO_PLL;
|
||||||
|
|
||||||
|
// printf( "Switching to HSE\n" );
|
||||||
|
Delay_Ms( 10 );
|
||||||
|
|
||||||
|
// Disable clock security system.
|
||||||
|
RCC->CTLR &= ~RCC_CSSON;
|
||||||
|
|
||||||
|
// Enable external crystal
|
||||||
|
RCC->CTLR |= RCC_HSEON;
|
||||||
|
|
||||||
|
// XXX NOTE: This is only used if you have a clock, not an oscillator.
|
||||||
|
// RCC->CTLR |= RCC_HSEBYP;
|
||||||
|
|
||||||
|
// Set System Clock Source to be 0.
|
||||||
|
RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | 0;
|
||||||
|
|
||||||
|
// Disable PLL
|
||||||
|
RCC->CTLR &= ~RCC_PLLON;
|
||||||
|
|
||||||
|
// Switch to HSE
|
||||||
|
RCC->CFGR0 |= RCC_PLLSRC;
|
||||||
|
|
||||||
|
// Enable PLL
|
||||||
|
RCC->CTLR |= RCC_PLLON;
|
||||||
|
|
||||||
|
// Wait for HSE to become ready.
|
||||||
|
while( !( RCC->CTLR & RCC_HSERDY) );
|
||||||
|
while( !( RCC->CTLR & RCC_PLLRDY) );
|
||||||
|
|
||||||
|
RCC->CFGR0 |= RCC_SW_1; // Switch system clock to PLL
|
||||||
|
// printf( "HSE Switched\n" );
|
||||||
|
Delay_Ms( 10 );
|
||||||
|
RCC->CTLR &= ~RCC_HSION;
|
||||||
|
Delay_Ms( 10 );
|
||||||
|
//printf( "HSI Off [%08lx %08lx]\n", RCC->CTLR, RCC->CFGR0 ); HSI Off [03035180 0001000a]
|
||||||
|
|
||||||
|
RCC->APB2PCENR |= RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOC |
|
||||||
|
RCC_APB2Periph_TIM1;
|
||||||
|
|
||||||
|
RCC->AHBPCENR |= RCC_AHBPeriph_DMA1;
|
||||||
|
|
||||||
|
funPinMode( PC3, GPIO_CFGLR_OUT_50Mhz_AF_PP ); // T1C3 on PC3
|
||||||
|
|
||||||
|
RCC->APB2PRSTR |= RCC_APB2Periph_TIM1;
|
||||||
|
RCC->APB2PRSTR &= ~RCC_APB2Periph_TIM1;
|
||||||
|
|
||||||
|
|
||||||
|
// Prescaler
|
||||||
|
TIM1->PSC = 0x0000;
|
||||||
|
|
||||||
|
// Auto Reload - sets period
|
||||||
|
TIM1->ATRLR = 17;
|
||||||
|
|
||||||
|
// Reload immediately
|
||||||
|
TIM1->SWEVGR |= TIM_UG;
|
||||||
|
|
||||||
|
// Enable CH1N output, positive pol
|
||||||
|
TIM1->CCER |= TIM_CC3E;
|
||||||
|
TIM1->CCER |= TIM_CC1E;
|
||||||
|
|
||||||
|
// Compare 3 = for output
|
||||||
|
// Modes:
|
||||||
|
// 0, 1, 2: Nothing
|
||||||
|
// 3: Flip
|
||||||
|
// 4, 5: Nothing
|
||||||
|
// 6: "Fast PWM mode 1"
|
||||||
|
// 7: Flipping (Further out)
|
||||||
|
#ifdef FM_TRANSMITTER_SWEEP
|
||||||
|
TIM1->CHCTLR2 = TIM_OC3M_0 | TIM_OC3M_1 | TIM_OC3PE | TIM_OC3FE;
|
||||||
|
#else
|
||||||
|
TIM1->CHCTLR2 = TIM_OC3M_2 | TIM_OC3M_1 | TIM_OC3PE | TIM_OC3FE;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Compare 1 = for triggering
|
||||||
|
TIM1->CHCTLR1 = TIM_OC1M_2 | TIM_OC1M_1 | TIM_OC1FE;
|
||||||
|
|
||||||
|
// Set the Capture Compare Register value to 50% initially
|
||||||
|
TIM1->CH3CVR = 4; // ACTUALLY Ignored typically it seems.
|
||||||
|
TIM1->CH1CVR = 0; // This triggers DMA.
|
||||||
|
|
||||||
|
// Enable TIM1 outputs
|
||||||
|
TIM1->BDTR |= TIM_MOE;
|
||||||
|
|
||||||
|
// Enable TIM1
|
||||||
|
TIM1->CTLR1 |= TIM_CEN;
|
||||||
|
|
||||||
|
TIM1->DMAINTENR = TIM_TDE | TIM_UDE; // Outputs DMA1_Channel5
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// Another try - use TIM2 to trigger DMA?
|
||||||
|
RCC->APB1PCENR |= RCC_APB1Periph_TIM2;
|
||||||
|
RCC->APB1PRSTR |= RCC_APB1Periph_TIM2;
|
||||||
|
RCC->APB1PRSTR &= ~RCC_APB1Periph_TIM2;
|
||||||
|
|
||||||
|
TIM2->PSC = 0x0000;
|
||||||
|
TIM2->ATRLR = 37;
|
||||||
|
TIM2->SWEVGR |= TIM_UG;
|
||||||
|
TIM2->BDTR |= TIM_MOE;
|
||||||
|
TIM2->CHCTLR1 = TIM_OC1M_2 | TIM_OC1M_1 | TIM_OC1FE;
|
||||||
|
TIM2->DMAINTENR |= TIM_UDE | TIM_TDE | TIM_TDE | TIM_CC1DE;
|
||||||
|
TIM2->CTLR1 |= TIM_CEN;
|
||||||
|
// TIM2_UP = DMA Channel 2.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DMA1_Channel5->PADDR = (uint32_t)&TIM1->CH3CVR;
|
||||||
|
DMA1_Channel5->MADDR = (uint32_t)sendbuff;
|
||||||
|
DMA1_Channel5->CNTR = 0;// sizeof( bufferset )/2; // Number of unique copies. (Don't start, yet!)
|
||||||
|
DMA1_Channel5->CFGR =
|
||||||
|
DMA_M2M_Disable |
|
||||||
|
DMA_Priority_VeryHigh |
|
||||||
|
DMA_PeripheralDataSize_HalfWord |
|
||||||
|
DMA_MemoryDataSize_Byte |
|
||||||
|
DMA_MemoryInc_Enable |
|
||||||
|
DMA_Mode_Circular | // OR DMA_Mode_Circular or DMA_Mode_Normal
|
||||||
|
DMA_DIR_PeripheralDST |
|
||||||
|
0;
|
||||||
|
//DMA_IT_TC | DMA_IT_HT; // Transmission Complete + Half Empty Interrupts.
|
||||||
|
|
||||||
|
// NVIC_EnableIRQ( DMA1_Channel3_IRQn );
|
||||||
|
|
||||||
|
int j;
|
||||||
|
for( j = 0; j < sizeof( sendbuff ); j++ )
|
||||||
|
{
|
||||||
|
sendbuff[j] = 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Enter critical section.
|
||||||
|
DMA1_Channel5->MADDR = (uint32_t)sendbuff;
|
||||||
|
DMA1_Channel5->CNTR = SENDBUFF_WORDS; // Number of unique uint16_t entries.
|
||||||
|
DMA1_Channel5->CFGR |= DMA_CFGR1_EN;
|
||||||
|
|
||||||
|
|
||||||
|
LoopFunction2();
|
||||||
|
}
|
||||||
|
|
||||||
+1
-1
Submodule ch32v/ch32v003fun updated: 4a959f4a77...44959b2ca1
@@ -0,0 +1,15 @@
|
|||||||
|
all : flash
|
||||||
|
|
||||||
|
TARGET:=adcfft
|
||||||
|
TARGET_MCU:=CH32V203G6U6
|
||||||
|
TARGET_MCU_PACKAGE:=CH32V203G6U6
|
||||||
|
CH32V003FUN:=../ch32v003fun/ch32v003fun
|
||||||
|
|
||||||
|
EXTRA_CFLAGS:=-Wno-unused-function -I../../lib -I../lib
|
||||||
|
|
||||||
|
include ../ch32v003fun/ch32v003fun/ch32v003fun.mk
|
||||||
|
|
||||||
|
flash : cv_flash
|
||||||
|
clean : cv_clean
|
||||||
|
rm -rf rf_data_gen chirpbuff.dat chirpbuff.h chirpbuffinfo.h
|
||||||
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# Example with an FFT for quickly figuring out where on the spectrum to look.
|
||||||
|
|
||||||
|
* Display on PB8/PB9 for SCL/SDA
|
||||||
|
* Analog is on PA7
|
||||||
@@ -0,0 +1,486 @@
|
|||||||
|
/**
|
||||||
|
|
||||||
|
MIT-like-non-ai-license
|
||||||
|
|
||||||
|
Copyright (c) 2024 Charles Lohr "CNLohr"
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the two following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
In addition the following restrictions apply:
|
||||||
|
|
||||||
|
1. The Software and any modifications made to it may not be used for the
|
||||||
|
purpose of training or improving machine learning algorithms, including but not
|
||||||
|
limited to artificial intelligence, natural language processing, or data
|
||||||
|
mining. This condition applies to any derivatives, modifications, or updates
|
||||||
|
based on the Software code. Any usage of the Software in an AI-training dataset
|
||||||
|
is considered a breach of this License.
|
||||||
|
|
||||||
|
2. The Software may not be included in any dataset used for training or
|
||||||
|
improving machine learning algorithms, including but not limited to artificial
|
||||||
|
intelligence, natural language processing, or data mining.
|
||||||
|
|
||||||
|
|
||||||
|
3. Any person or organization found to be in violation of these restrictions
|
||||||
|
will be subject to legal action and may be held liable for any damages
|
||||||
|
resulting from such use.
|
||||||
|
|
||||||
|
If any term is unenforcable, other terms remain in-force.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
// NOT LORA!!! -- but experimenting with the possibility of rx.
|
||||||
|
|
||||||
|
// SETUP INSTRUCTIONS:
|
||||||
|
// (1) `make` in the optionbytes folder to configure `RESET` correctly.
|
||||||
|
// (2) Create a tone (if using the funprog, ../ch32v003fun/minichlink/minichlink -X ECLK 1:235:189:9:3 for 27.48387097MHz
|
||||||
|
// (2) or, for 24.387096762MHz - ../ch32v003fun/minichlink/minichlink -X ECLK 1:150:49:8:3
|
||||||
|
|
||||||
|
/* More notes
|
||||||
|
|
||||||
|
* Minimum sample time with DMA = fCPU / 28 (5.14MHz)
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "ch32v003fun.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define SH1107_128x128
|
||||||
|
#define SSD1306_REMAP_I2C
|
||||||
|
#include "ssd1306_i2c.h"
|
||||||
|
#include "ssd1306.h"
|
||||||
|
|
||||||
|
#define FIX_FFT_IMPLEMENTATION
|
||||||
|
#include "fix_fft.h"
|
||||||
|
|
||||||
|
/* General note:
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define Q 256
|
||||||
|
|
||||||
|
#define TARGET_BIN 51
|
||||||
|
|
||||||
|
#define PWM_PERIOD (31-1) //For 27.0MHz, use 36MHz if quadrature -- It appears to be good for *244 in the table? WHY 26MHz???!?!!?
|
||||||
|
|
||||||
|
#define ADC_BUFFSIZE 512
|
||||||
|
volatile uint16_t adc_buffer[ADC_BUFFSIZE];
|
||||||
|
|
||||||
|
void SetupADC()
|
||||||
|
{
|
||||||
|
// XXX TODO -look into PGA
|
||||||
|
// XXX TODO - Look into tag-teaming the ADCs
|
||||||
|
|
||||||
|
// PA7 is analog input chl 7
|
||||||
|
GPIOA->CFGLR &= ~(0xf<<(4*7)); // CNF = 00: Analog, MODE = 00: Input
|
||||||
|
|
||||||
|
// ADC CLK is chained off of APB2.
|
||||||
|
|
||||||
|
// Reset the ADC to init all regs
|
||||||
|
RCC->APB2PRSTR |= RCC_APB2Periph_ADC1;
|
||||||
|
RCC->APB2PRSTR &= ~RCC_APB2Periph_ADC1;
|
||||||
|
|
||||||
|
// ADCCLK = 12 MHz => RCC_ADCPRE divide by 4
|
||||||
|
RCC->CFGR0 &= ~RCC_ADCPRE; // Clear out the bis in case they were set
|
||||||
|
RCC->CFGR0 |= RCC_ADCPRE_DIV2; // Fastest possible (divide-by-2) NOTE: This is OUTSIDE the specified value in the datasheet.
|
||||||
|
|
||||||
|
// Set up single conversion on chl 7
|
||||||
|
ADC1->RSQR1 = 0;
|
||||||
|
ADC1->RSQR2 = 0;
|
||||||
|
ADC1->RSQR3 = 7; // 0-9 for 8 ext inputs and two internals
|
||||||
|
|
||||||
|
// Not using injection group.
|
||||||
|
|
||||||
|
// Sampling time for channels. Careful: This has PID tuning implications.
|
||||||
|
// Note that with 3 and 3,the full loop (and injection) runs at 138kHz.
|
||||||
|
ADC1->SAMPTR2 = (0<<(3*7));
|
||||||
|
|
||||||
|
// Turn on ADC and set rule group to sw trig
|
||||||
|
// 0 = Use TRGO event for Timer 1 to fire ADC rule.
|
||||||
|
ADC1->CTLR2 = ADC_ADON | ADC_EXTTRIG | ADC_DMA;
|
||||||
|
|
||||||
|
// Reset calibration
|
||||||
|
ADC1->CTLR2 |= ADC_RSTCAL;
|
||||||
|
while(ADC1->CTLR2 & ADC_RSTCAL);
|
||||||
|
|
||||||
|
// Calibrate ADC
|
||||||
|
ADC1->CTLR2 |= ADC_CAL;
|
||||||
|
while(ADC1->CTLR2 & ADC_CAL);
|
||||||
|
|
||||||
|
// ADC_SCAN: Allow scanning.
|
||||||
|
ADC1->CTLR1 = /*ADC_Pga_64 | */ADC_SCAN;
|
||||||
|
|
||||||
|
|
||||||
|
// Turn on DMA
|
||||||
|
RCC->AHBPCENR |= RCC_AHBPeriph_DMA1;
|
||||||
|
|
||||||
|
//DMA1_Channel1 is for ADC
|
||||||
|
DMA1_Channel1->PADDR = (uint32_t)&ADC1->RDATAR;
|
||||||
|
DMA1_Channel1->MADDR = (uint32_t)adc_buffer;
|
||||||
|
DMA1_Channel1->CNTR = ADC_BUFFSIZE;
|
||||||
|
DMA1_Channel1->CFGR =
|
||||||
|
DMA_M2M_Disable |
|
||||||
|
DMA_Priority_VeryHigh |
|
||||||
|
DMA_MemoryDataSize_HalfWord |
|
||||||
|
DMA_PeripheralDataSize_HalfWord |
|
||||||
|
DMA_MemoryInc_Enable |
|
||||||
|
DMA_Mode_Circular |
|
||||||
|
DMA_DIR_PeripheralSRC;
|
||||||
|
|
||||||
|
// Turn on DMA channel 1
|
||||||
|
DMA1_Channel1->CFGR |= DMA_CFGR1_EN;
|
||||||
|
|
||||||
|
// Enable continuous conversion and DMA
|
||||||
|
ADC1->CTLR2 |= ADC_DMA; // | ADC_CONT;
|
||||||
|
|
||||||
|
// start conversion
|
||||||
|
ADC1->CTLR2 |= ADC_SWSTART;// | ADC_CONT;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetupTimer1()
|
||||||
|
{
|
||||||
|
// Enable Timer 1
|
||||||
|
RCC->APB2PRSTR |= RCC_APB2Periph_TIM1;
|
||||||
|
RCC->APB2PRSTR &= ~RCC_APB2Periph_TIM1;
|
||||||
|
|
||||||
|
TIM1->PSC = 0; // Prescalar to 0x0000 (so, 48MHz base clock)
|
||||||
|
TIM1->ATRLR = PWM_PERIOD;
|
||||||
|
|
||||||
|
#ifdef PWM_OUTPUT
|
||||||
|
// PA10 = T1CH3.
|
||||||
|
GPIOA->CFGHR &= ~(0xf<<(4*2));
|
||||||
|
GPIOA->CFGHR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*2);
|
||||||
|
|
||||||
|
TIM1->CCER = TIM_CC3E | TIM_CC3P;
|
||||||
|
TIM1->CHCTLR2 = TIM_OC3M_2 | TIM_OC3M_1;
|
||||||
|
TIM1->CH3CVR = 5; // Actual duty cycle (Off to begin with)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TIM1->CCER = TIM_CC1E;
|
||||||
|
TIM1->CHCTLR1 = TIM_OC1M_2 | TIM_OC1M_1;
|
||||||
|
TIM1->CH1CVR = 1;
|
||||||
|
|
||||||
|
// Setup TRGO to trigger for ADC (NOTE: Not on the 203! TIM1_TRGO is only connected to injection)
|
||||||
|
//TIM1->CTLR2 = TIM_MMS_1;
|
||||||
|
|
||||||
|
// Enable TIM1 outputs
|
||||||
|
TIM1->BDTR = TIM_MOE;
|
||||||
|
TIM1->CTLR1 = TIM_CEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void InnerLoop() __attribute__((noreturn));
|
||||||
|
|
||||||
|
|
||||||
|
void InnerLoop()
|
||||||
|
{
|
||||||
|
//int i = 0;
|
||||||
|
//int q = 0;
|
||||||
|
int tpl = 0;
|
||||||
|
|
||||||
|
// Timer goes backwards when we are moving forwards.
|
||||||
|
volatile uint16_t * adc_buffer_end = 0;
|
||||||
|
volatile uint16_t * adc_buffer_top = adc_buffer + ADC_BUFFSIZE;
|
||||||
|
volatile uint16_t * adc = adc_buffer;
|
||||||
|
|
||||||
|
int frcnt = 0;
|
||||||
|
|
||||||
|
int tstart = 0;
|
||||||
|
|
||||||
|
int16_t shadowbuff[Q+16];
|
||||||
|
|
||||||
|
int shadowplace = 0;
|
||||||
|
#define SCALEUP 6
|
||||||
|
#define SHADOWSTORE(X) shadowbuff[frcnt+X] = t;
|
||||||
|
|
||||||
|
while( 1 )
|
||||||
|
{
|
||||||
|
tpl = ADC_BUFFSIZE - DMA1_Channel1->CNTR; // Warning, sometimes this is == to the base, or == 0 (i.e. might be 256, if top is 255)
|
||||||
|
|
||||||
|
tpl += ADC_BUFFSIZE;
|
||||||
|
tpl = (tpl & (ADC_BUFFSIZE-1));
|
||||||
|
if( tpl == ADC_BUFFSIZE ) tpl = 0;
|
||||||
|
|
||||||
|
adc_buffer_end = adc_buffer + ( ( tpl / 4) * 4 );
|
||||||
|
//printf( "%3d %4d %d %04x\n", DMA1_Channel1->CNTR, TIM1->CNT, ADC1->RDATAR, ADC1->STATR );
|
||||||
|
while( adc != adc_buffer_end )
|
||||||
|
{
|
||||||
|
|
||||||
|
int32_t t = adc[0]; SHADOWSTORE(0);
|
||||||
|
//i += t; q += t;
|
||||||
|
t = adc[1]; SHADOWSTORE(1);
|
||||||
|
//i -= t; q += t;
|
||||||
|
t = adc[2]; SHADOWSTORE(2);
|
||||||
|
//i -= t; q -= t;
|
||||||
|
t = adc[3]; SHADOWSTORE(3);
|
||||||
|
//i += t; q -= t;
|
||||||
|
adc += 4;
|
||||||
|
frcnt += 4;
|
||||||
|
|
||||||
|
if( adc == adc_buffer_top ) adc = adc_buffer;
|
||||||
|
if( frcnt >= Q ) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if( frcnt >= Q )
|
||||||
|
{
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
|
||||||
|
#ifdef DUMPBUFF
|
||||||
|
int j;
|
||||||
|
for( j = 0; j < Q; j++ )
|
||||||
|
printf( "%d,%d\n", j, shadowbuff[j] );
|
||||||
|
#endif
|
||||||
|
#ifdef QUADRATURE
|
||||||
|
int ti = i>>3;
|
||||||
|
int tq = q>>3;
|
||||||
|
int is = (ti*ti + tq*tq)>>8;
|
||||||
|
#else
|
||||||
|
int is = i>>2;
|
||||||
|
#endif
|
||||||
|
int s = 1<<( ( 32 - __builtin_clz(is) )/2);
|
||||||
|
s = (s + is/s)/2;
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef TIGHT_OUT
|
||||||
|
printf( "%d\n", s );
|
||||||
|
#elif defined( PWM_OUTPUT )
|
||||||
|
int tv = (s>>PWM_OUTPUT) + (PWM_PERIOD/2);
|
||||||
|
if( tv < 0 ) tv = 0;
|
||||||
|
if( tv >= PWM_PERIOD ) tv = PWM_PERIOD-1;
|
||||||
|
TIM1->CH3CVR = tv;
|
||||||
|
#else
|
||||||
|
|
||||||
|
printf( "%8d I:%7d Q:%7d [%d %d %d %d] / %d\n",s, i ,q, adc_buffer[0], adc_buffer[1], adc_buffer[2], adc_buffer[3], (int)(SysTick->CNT - tstart) );
|
||||||
|
#endif
|
||||||
|
//printf( "%d\n", s );
|
||||||
|
#endif
|
||||||
|
int k;
|
||||||
|
|
||||||
|
// printf( "Data: " );
|
||||||
|
for( k = 0; k < Q; k++ )
|
||||||
|
{
|
||||||
|
// printf( "%d, ",shadowbuff[k] );
|
||||||
|
shadowbuff[k] = (shadowbuff[k]<<SCALEUP)-32768;
|
||||||
|
}
|
||||||
|
//printf( "\n" );
|
||||||
|
|
||||||
|
int osb = shadowbuff[0];
|
||||||
|
int16_t FSQ[Q] = { 0 };
|
||||||
|
for( k = 0; k < 128; k++ )
|
||||||
|
{
|
||||||
|
ssd1306_drawPixel( k, ((shadowbuff[k+128]-osb)>>(SCALEUP+1))+40, 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t imag[Q] = { 0 };
|
||||||
|
|
||||||
|
int r =
|
||||||
|
fix_fft(shadowbuff, imag, 8, 0);
|
||||||
|
|
||||||
|
//fix_fftr(shadowbuff, 7 /*1<<7 = 128 bins wide*/, 0);
|
||||||
|
//fix_fft(shadowbuff, FSQ, 8 /*1<<7 = 128 bins wide*/, 0);
|
||||||
|
|
||||||
|
// printf( "FFT:
|
||||||
|
// for( k = 0; k < 128; k++ )
|
||||||
|
// {
|
||||||
|
// }
|
||||||
|
|
||||||
|
int targbin = TARGET_BIN;
|
||||||
|
int targv = 0;
|
||||||
|
int maxbin = 0;
|
||||||
|
int maxbinv = 0;
|
||||||
|
|
||||||
|
|
||||||
|
for( k = 0; k < 128; k++ )
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
int s = shadowbuff[k] * shadowbuff[k] + FSQ[k]*FSQ[k];
|
||||||
|
//if( s == 0 ) continue;
|
||||||
|
int x = 1<<( ( 32 - __builtin_clz(s) )/2);
|
||||||
|
x = (x + i/x)/2;
|
||||||
|
x = (x + i/x)/2; //Not really needed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// for real
|
||||||
|
// int x = shadowbuff[(k>>1) | ((k&64)>>6)];
|
||||||
|
// For faked imag
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
int s = shadowbuff[k] * shadowbuff[k] + shadowbuff[255-k]*shadowbuff[255-k];
|
||||||
|
//if( s == 0 ) continue;
|
||||||
|
int x = 1<<( ( 32 - __builtin_clz(s) )/2);
|
||||||
|
x = (x + s/x)/2;
|
||||||
|
x = (x + s/x)/2; //Not really needed.
|
||||||
|
x = (x + s/x)/2; //Not really needed.
|
||||||
|
|
||||||
|
//x = shadowbuff[ k ];
|
||||||
|
//x = s >> 8;
|
||||||
|
|
||||||
|
x = x >> 2;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int s = shadowbuff[k] * shadowbuff[k] + imag[k]*imag[k];
|
||||||
|
//if( s == 0 ) continue;
|
||||||
|
int x = 1<<( ( 32 - __builtin_clz(s) )/2);
|
||||||
|
x = (x + s/x)/2;
|
||||||
|
x = (x + s/x)/2; //Not really needed.
|
||||||
|
x = (x + s/x)/2; //Not really needed.
|
||||||
|
|
||||||
|
//x = shadowbuff[ k ];
|
||||||
|
//x = s >> 8;
|
||||||
|
|
||||||
|
//x = x >> 2;
|
||||||
|
if( x > maxbinv && k != 0) { maxbinv = x; maxbin = k; }
|
||||||
|
if( k == targbin ) targv = x;
|
||||||
|
|
||||||
|
x++;
|
||||||
|
if( x < 0 ) x = 0;
|
||||||
|
if( x > 127 ) x = 127;
|
||||||
|
if( x != 0 )
|
||||||
|
ssd1306_drawFastVLine( k, 127-x, x, 1 );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static int tbhist[128];
|
||||||
|
static int tbhead = 0;
|
||||||
|
tbhist[tbhead++] = targv;
|
||||||
|
if( tbhead == 128 ) tbhead = 0;
|
||||||
|
|
||||||
|
char cts[32];
|
||||||
|
snprintf( cts, sizeof(cts), "%5d%5d@%d", osb, targv, targbin );
|
||||||
|
ssd1306_drawstr( 0, 0, cts, 1 );
|
||||||
|
snprintf( cts, sizeof(cts), "P:%d B%3d/%4d", PWM_PERIOD, maxbin, maxbinv );
|
||||||
|
ssd1306_drawstr( 0, 8, cts, 1 );
|
||||||
|
|
||||||
|
for( k = 0; k < 128; k++ )
|
||||||
|
{
|
||||||
|
ssd1306_drawPixel( k, 105-tbhist[(tbhead-k+256)%128]/2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset( shadowbuff, 0, sizeof( shadowbuff ) );
|
||||||
|
|
||||||
|
ssd1306_refresh();
|
||||||
|
ssd1306_setbuf(0);
|
||||||
|
|
||||||
|
frcnt = 0;
|
||||||
|
// i = 0;
|
||||||
|
// q = 0;
|
||||||
|
tpl = ADC_BUFFSIZE - DMA1_Channel1->CNTR;
|
||||||
|
adc = adc_buffer + ( ( tpl / 4) * 4 );
|
||||||
|
tstart = SysTick->CNT;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
Delay_Us( 100 );
|
||||||
|
int end = DMA1_Channel1->CNTR;
|
||||||
|
int v0 = adc_buffer[0];
|
||||||
|
int v1 = adc_buffer[1];
|
||||||
|
int v2 = adc_buffer[2];
|
||||||
|
int v3 = adc_buffer[3];
|
||||||
|
printf( "%d %d %d %d %d\n", (uint8_t)(start-end), v0, v1, v2, v3 );
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
SystemInit();
|
||||||
|
|
||||||
|
SysTick->CTLR = (1<<2) | 1; // HCLK
|
||||||
|
Delay_Ms(100);
|
||||||
|
|
||||||
|
printf( "System On\n" );
|
||||||
|
|
||||||
|
RCC->CTLR |= RCC_HSEON;
|
||||||
|
while( ! ( RCC->CTLR & RCC_HSERDY ) );
|
||||||
|
|
||||||
|
RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_HSE;
|
||||||
|
|
||||||
|
RCC->CTLR &= ~RCC_PLLON;
|
||||||
|
|
||||||
|
// Switch PLL to HSE.
|
||||||
|
RCC->CFGR0 |= RCC_PLLSRC;
|
||||||
|
|
||||||
|
// x18; 8MHz x 18 = 144 MHz
|
||||||
|
RCC->CFGR0 &= ~RCC_PPRE2; // No divisor on APB1/2
|
||||||
|
RCC->CFGR0 &= ~RCC_PPRE1;
|
||||||
|
RCC->CFGR0 |= RCC_PLLMULL_0 | RCC_PLLMULL_1 | RCC_PLLMULL_2 | RCC_PLLMULL_3;
|
||||||
|
|
||||||
|
RCC->CTLR |= RCC_PLLON;
|
||||||
|
|
||||||
|
// Switch to PLL
|
||||||
|
RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_PLL;
|
||||||
|
|
||||||
|
// Disable HSI
|
||||||
|
RCC->CTLR &= ~(RCC_HSION);
|
||||||
|
|
||||||
|
Delay_Ms(10);
|
||||||
|
|
||||||
|
|
||||||
|
printf( "CTLR: %08x / CFGR0: %08x\n", (RCC->CTLR), (RCC->CFGR0) );
|
||||||
|
RCC->AHBPCENR |= 3; //DMA2EN | DMA1EN
|
||||||
|
RCC->APB2PCENR |= RCC_APB2Periph_TIM1 | RCC_APB2Periph_ADC1 | RCC_APB2Periph_ADC2 | 0x07; // Enable all GPIO
|
||||||
|
RCC->APB1PCENR |= RCC_APB1Periph_TIM2;
|
||||||
|
|
||||||
|
SetupADC();
|
||||||
|
|
||||||
|
printf( "Setting up OLED.\n" );
|
||||||
|
ssd1306_i2c_setup();
|
||||||
|
uint8_t ret = ssd1306_i2c_init();
|
||||||
|
ssd1306_init();
|
||||||
|
ssd1306_setbuf(0);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
int i = 0;
|
||||||
|
int k = 0;
|
||||||
|
int frame = 0;
|
||||||
|
while( 1)
|
||||||
|
{
|
||||||
|
// ssd1306_drawLine( (frame)%128, (0)%128, (0)%128, (127-frame)%128, 1 );
|
||||||
|
ssd1306_drawstr( frame%128, frame%128, "hello", 1 );
|
||||||
|
|
||||||
|
ssd1306_refresh();
|
||||||
|
ssd1306_setbuf(0);
|
||||||
|
frame++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// turn on the op-amp
|
||||||
|
EXTEN->EXTEN_CTR |= EXTEN_OPA_EN;
|
||||||
|
|
||||||
|
// select op-amp pos pin: 0 = PA2, 1 = PD7
|
||||||
|
EXTEN->EXTEN_CTR |= EXTEN_OPA_PSEL;
|
||||||
|
|
||||||
|
// select op-amp neg pin: 0 = PA1, 1 = PD0
|
||||||
|
EXTEN->EXTEN_CTR |= EXTEN_OPA_NSEL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
printf( "ADC Setup\n" );
|
||||||
|
|
||||||
|
SetupTimer1();
|
||||||
|
|
||||||
|
printf( "Timer 1 setup\n" );
|
||||||
|
|
||||||
|
InnerLoop();
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#ifndef _FUNCONFIG_H
|
||||||
|
#define _FUNCONFIG_H
|
||||||
|
|
||||||
|
#define FUNCONF_USE_DEBUGPRINTF 1
|
||||||
|
#define FUNCONF_USE_UARTPRINTF 0
|
||||||
|
#define FUNCONF_USE_HSE 1
|
||||||
|
#define FUNCONF_SYSTICK_USE_HCLK 1
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
all : flash
|
||||||
|
|
||||||
|
TARGET:=adcgoertzel
|
||||||
|
TARGET_MCU:=CH32V203G6U6
|
||||||
|
TARGET_MCU_PACKAGE:=CH32V203G6U6
|
||||||
|
CH32V003FUN:=../ch32v003fun/ch32v003fun
|
||||||
|
|
||||||
|
EXTRA_CFLAGS:=-Wno-unused-function -I../../lib -I../lib -I../ch32v003fun/examples_v20x/otg_device -I.
|
||||||
|
|
||||||
|
include ../ch32v003fun/ch32v003fun/ch32v003fun.mk
|
||||||
|
|
||||||
|
flash : cv_flash
|
||||||
|
clean : cv_clean
|
||||||
|
rm -rf rf_data_gen chirpbuff.dat chirpbuff.h chirpbuffinfo.h
|
||||||
|
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# How to use this
|
||||||
|
|
||||||
|
* Display on PB8/PB9 for SCL/SDA
|
||||||
|
* Analog is on PA7
|
||||||
|
|
||||||
|
First, use FFT, then look in the middle for the highest bin @. This is the # of pixels from the left side of the screen you are.
|
||||||
|
|
||||||
|
Then edit ttest.c,
|
||||||
|
|
||||||
|
```
|
||||||
|
g_goertzel_omega_per_sample = (47.0/256) * 3.1415926535*2.0*65536;
|
||||||
|
```
|
||||||
|
|
||||||
|
where 47.0 in this case is the bin # from the FFT.
|
||||||
|
|
||||||
|
Change this, `make test`
|
||||||
|
|
||||||
|
Then copy the values:
|
||||||
|
const int32_t g_goertzel_omega_per_sample = 75599;
|
||||||
|
const int32_t g_goertzel_coefficient = 870257651;
|
||||||
|
const int32_t g_goertzel_coefficient_s = 1963246708;
|
||||||
|
|
||||||
|
into the top of adcgoertzel.c
|
||||||
|
|
||||||
|
|
||||||
|
TODO: TODO: Test HTML values.
|
||||||
|
|
||||||
|
TODO: TODO: g_goertzel_omega_per_sample lokos WRONG.
|
||||||
|
|
||||||
@@ -0,0 +1,723 @@
|
|||||||
|
/**
|
||||||
|
|
||||||
|
MIT-like-non-ai-license
|
||||||
|
|
||||||
|
Copyright (c) 2024 Charles Lohr "CNLohr"
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the two following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
In addition the following restrictions apply:
|
||||||
|
|
||||||
|
1. The Software and any modifications made to it may not be used for the
|
||||||
|
purpose of training or improving machine learning algorithms, including but not
|
||||||
|
limited to artificial intelligence, natural language processing, or data
|
||||||
|
mining. This condition applies to any derivatives, modifications, or updates
|
||||||
|
based on the Software code. Any usage of the Software in an AI-training dataset
|
||||||
|
is considered a breach of this License.
|
||||||
|
|
||||||
|
2. The Software may not be included in any dataset used for training or
|
||||||
|
improving machine learning algorithms, including but not limited to artificial
|
||||||
|
intelligence, natural language processing, or data mining.
|
||||||
|
|
||||||
|
|
||||||
|
3. Any person or organization found to be in violation of these restrictions
|
||||||
|
will be subject to legal action and may be held liable for any damages
|
||||||
|
resulting from such use.
|
||||||
|
|
||||||
|
If any term is unenforcable, other terms remain in-force.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
|
**/
|
||||||
|
|
||||||
|
// NOT LORA!!! -- but experimenting with the possibility of rx.
|
||||||
|
|
||||||
|
// SETUP INSTRUCTIONS:
|
||||||
|
// (1) `make` in the optionbytes folder to configure `RESET` correctly.
|
||||||
|
// (2) Create a tone (if using the funprog, ../ch32v003fun/minichlink/minichlink -X ECLK 1:235:189:9:3 for 27.48387097MHz
|
||||||
|
// (2) or, for 24.387096762MHz - ../ch32v003fun/minichlink/minichlink -X ECLK 1:150:49:8:3
|
||||||
|
|
||||||
|
/* More notes
|
||||||
|
|
||||||
|
* Minimum sample time with DMA = fCPU / 28 (5.14MHz)
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "ch32v003fun.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#define SH1107_128x128
|
||||||
|
#define SSD1306_REMAP_I2C
|
||||||
|
//#define PWM_OUTPUT
|
||||||
|
#define ENABLE_OLED
|
||||||
|
#define PROFILING_PIN PA0
|
||||||
|
|
||||||
|
#include "ssd1306_i2c.h"
|
||||||
|
#include "ssd1306.h"
|
||||||
|
#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];
|
||||||
|
|
||||||
|
int g_volume_pwm = 127; // 0 - 127 (100%) (but you can go over 100)
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
int g_pwm_period = (30-1);
|
||||||
|
int g_goertzel_buffer = (752);
|
||||||
|
int g_exactcompute = (0);
|
||||||
|
int32_t g_goertzel_omega_per_sample = 2485087396; // 0.368351 of whole per step / 27.031915MHz
|
||||||
|
int32_t g_goertzel_coefficient = -1453756170;
|
||||||
|
int32_t g_goertzel_coefficient_s = 1580594514;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
int g_pwm_period = (30-1);
|
||||||
|
int g_goertzel_buffer = (180);
|
||||||
|
int g_exactcompute = (0);
|
||||||
|
int32_t g_goertzel_omega_per_sample = 5509657063; // 0.816667 of whole per step / 0.880000MHz
|
||||||
|
int32_t g_goertzel_coefficient = 873460290;
|
||||||
|
int32_t g_goertzel_coefficient_s = -1961823932;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
int g_pwm_period = (31-1);
|
||||||
|
int g_goertzel_buffer = (412);
|
||||||
|
int g_exactcompute = (0);
|
||||||
|
const int32_t g_goertzel_omega_per_sample = 1670254667; // 0.247573 of whole per step / 1.150016MHz
|
||||||
|
const int32_t g_goertzel_coefficient = 32748822;
|
||||||
|
const int32_t g_goertzel_coefficient_s = 2147233926;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
int g_pwm_period = (30-1);
|
||||||
|
int g_goertzel_buffer = (576);
|
||||||
|
int g_exactcompute = (0);
|
||||||
|
int32_t g_goertzel_omega_per_sample = 1264972285; // 0.187500 of whole per step / 90.300000MHz
|
||||||
|
int32_t g_goertzel_coefficient = 821806413;
|
||||||
|
int32_t g_goertzel_coefficient_s = 1984016189;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
int g_pwm_period = (30-1);
|
||||||
|
int g_goertzel_buffer = (320);
|
||||||
|
int g_exactcompute = (0);
|
||||||
|
const int32_t g_goertzel_omega_per_sample = 990894956; // 0.146875 of whole per step / 101.505000MHz
|
||||||
|
const int32_t g_goertzel_coefficient = 1296126516;
|
||||||
|
const int32_t g_goertzel_coefficient_s = 1712233066;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
int g_pwm_period = (30-1);
|
||||||
|
int g_goertzel_buffer = (384);
|
||||||
|
int g_exactcompute = (0);
|
||||||
|
const int32_t g_goertzel_omega_per_sample = 4251712402; // 0.630208 of whole per step / 27.025000MHz
|
||||||
|
const int32_t g_goertzel_coefficient = -1468003291;
|
||||||
|
const int32_t g_goertzel_coefficient_s = -1567371161;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
int g_pwm_period = (30-1);
|
||||||
|
int g_goertzel_buffer = (336);
|
||||||
|
int g_exactcompute = (0);
|
||||||
|
const int32_t g_goertzel_omega_per_sample = 1827182189; // 0.270833 of whole per step / 89.900000MHz
|
||||||
|
const int32_t g_goertzel_coefficient = -280302863;
|
||||||
|
const int32_t g_goertzel_coefficient_s = 2129111628;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
int intensity_average = 1;
|
||||||
|
|
||||||
|
#define LOG_GOERTZEL_LIST 512
|
||||||
|
int32_t qibaselogs[LOG_GOERTZEL_LIST];
|
||||||
|
volatile int qibaselogs_head;
|
||||||
|
|
||||||
|
void SetupADC()
|
||||||
|
{
|
||||||
|
// XXX TODO -look into PGA
|
||||||
|
// XXX TODO - Look into tag-teaming the ADCs
|
||||||
|
|
||||||
|
// PDA is analog input chl 7
|
||||||
|
GPIOA->CFGLR &= ~(0xf<<(4*7)); // CNF = 00: Analog, MODE = 00: Input
|
||||||
|
|
||||||
|
// ADC CLK is chained off of APB2.
|
||||||
|
|
||||||
|
// Reset the ADC to init all regs
|
||||||
|
RCC->APB2PRSTR |= RCC_APB2Periph_ADC1;
|
||||||
|
RCC->APB2PRSTR &= ~RCC_APB2Periph_ADC1;
|
||||||
|
|
||||||
|
// ADCCLK = 12 MHz => RCC_ADCPRE divide by 4
|
||||||
|
RCC->CFGR0 &= ~RCC_ADCPRE; // Clear out the bis in case they were set
|
||||||
|
RCC->CFGR0 |= RCC_ADCPRE_DIV2; // Fastest possible (divide-by-2) NOTE: This is OUTSIDE the specified value in the datasheet.
|
||||||
|
|
||||||
|
// Set up single conversion on chl 7
|
||||||
|
ADC1->RSQR1 = 0;
|
||||||
|
ADC1->RSQR2 = 0;
|
||||||
|
ADC1->RSQR3 = 7; // 0-9 for 8 ext inputs and two internals
|
||||||
|
|
||||||
|
// Not using injection group.
|
||||||
|
|
||||||
|
// Sampling time for channels. Careful: This has PID tuning implications.
|
||||||
|
// Note that with 3 and 3,the full loop (and injection) runs at 138kHz.
|
||||||
|
ADC1->SAMPTR2 = (0<<(3*7));
|
||||||
|
|
||||||
|
// Turn on ADC and set rule group to sw trig
|
||||||
|
// 0 = Use TRGO event for Timer 1 to fire ADC rule.
|
||||||
|
ADC1->CTLR2 = ADC_ADON | ADC_EXTTRIG | ADC_DMA;
|
||||||
|
|
||||||
|
// Reset calibration
|
||||||
|
ADC1->CTLR2 |= ADC_RSTCAL;
|
||||||
|
while(ADC1->CTLR2 & ADC_RSTCAL);
|
||||||
|
|
||||||
|
// Calibrate ADC
|
||||||
|
ADC1->CTLR2 |= ADC_CAL;
|
||||||
|
while(ADC1->CTLR2 & ADC_CAL);
|
||||||
|
|
||||||
|
// ADC_SCAN: Allow scanning.
|
||||||
|
ADC1->CTLR1 = ADC_Pga_64 | ADC_SCAN;
|
||||||
|
|
||||||
|
|
||||||
|
// Turn on DMA
|
||||||
|
RCC->AHBPCENR |= RCC_AHBPeriph_DMA1;
|
||||||
|
|
||||||
|
//DMA1_Channel1 is for ADC
|
||||||
|
DMA1_Channel1->PADDR = (uint32_t)&ADC1->RDATAR;
|
||||||
|
DMA1_Channel1->MADDR = (uint32_t)adc_buffer;
|
||||||
|
DMA1_Channel1->CNTR = ADC_BUFFSIZE;
|
||||||
|
DMA1_Channel1->CFGR =
|
||||||
|
DMA_M2M_Disable |
|
||||||
|
DMA_Priority_VeryHigh |
|
||||||
|
DMA_MemoryDataSize_HalfWord |
|
||||||
|
DMA_PeripheralDataSize_HalfWord |
|
||||||
|
DMA_MemoryInc_Enable |
|
||||||
|
DMA_Mode_Circular |
|
||||||
|
DMA_DIR_PeripheralSRC;
|
||||||
|
|
||||||
|
// NVIC_SetPriority( DMA1_Channel1_IRQn, 0<<4 ); //We don't need to tweak priority.
|
||||||
|
NVIC_EnableIRQ( DMA1_Channel1_IRQn );
|
||||||
|
DMA1_Channel1->CFGR |= DMA_CFGR1_EN | DMA_IT_TC | DMA_IT_HT; // Transmission Complete + Half Empty Interrupts.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Turn on DMA channel 1
|
||||||
|
DMA1_Channel1->CFGR |= DMA_CFGR1_EN;
|
||||||
|
|
||||||
|
// Enable continuous conversion and DMA
|
||||||
|
ADC1->CTLR2 |= ADC_DMA; // | ADC_CONT;
|
||||||
|
|
||||||
|
// start conversion
|
||||||
|
ADC1->CTLR2 |= ADC_SWSTART;// | ADC_CONT;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetupTimer1()
|
||||||
|
{
|
||||||
|
// Enable Timer 1
|
||||||
|
RCC->APB2PRSTR |= RCC_APB2Periph_TIM1;
|
||||||
|
RCC->APB2PRSTR &= ~RCC_APB2Periph_TIM1;
|
||||||
|
|
||||||
|
TIM1->PSC = 0; // Prescalar to 0x0000 (so, 48MHz base clock)
|
||||||
|
TIM1->ATRLR = g_pwm_period;
|
||||||
|
|
||||||
|
#ifdef PWM_OUTPUT
|
||||||
|
// PA9 = T1CH2.
|
||||||
|
funPinMode( PA9, GPIO_CFGLR_OUT_2Mhz_AF_PP );
|
||||||
|
|
||||||
|
TIM1->CCER = TIM_CC2E | TIM_CC2P;
|
||||||
|
TIM1->CHCTLR1 |= TIM_OC2M_2 | TIM_OC2M_1 | TIM_OC2FE;
|
||||||
|
TIM1->CH2CVR = 5; // Set duty cycle somewhere random.
|
||||||
|
|
||||||
|
// Enable TIM1 outputs
|
||||||
|
TIM1->BDTR |= 0xc000;//TIM_MOE;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TIM1->CCER |= TIM_CC1E;
|
||||||
|
TIM1->CHCTLR1 |= TIM_OC1M_2 | TIM_OC1M_1;
|
||||||
|
TIM1->CH1CVR = 1;
|
||||||
|
|
||||||
|
// Setup TRGO to trigger for ADC (NOTE: Not on the 203! TIM1_TRGO is only connected to injection)
|
||||||
|
//TIM1->CTLR2 = TIM_MMS_1;
|
||||||
|
|
||||||
|
// Enable TIM1 outputs
|
||||||
|
TIM1->BDTR = TIM_MOE;
|
||||||
|
TIM1->CTLR1 = TIM_CEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void InnerLoop() __attribute__((noreturn));
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t tc;
|
||||||
|
|
||||||
|
|
||||||
|
volatile uint16_t * adc_tail = adc_buffer;
|
||||||
|
|
||||||
|
uint32_t g_goertzel_samples;
|
||||||
|
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;
|
||||||
|
uint32_t g_accumulate_over_window;
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
// Timer goes backwards when we are moving forwards.
|
||||||
|
volatile uint16_t * adc_buffer_end = 0;
|
||||||
|
volatile uint16_t * adc_buffer_top = adc_buffer + ADC_BUFFSIZE;
|
||||||
|
|
||||||
|
int32_t goertzel_coefficient = g_goertzel_coefficient;
|
||||||
|
int32_t goertzelp2 = g_goertzelp2;
|
||||||
|
int32_t goertzelp = g_goertzelp;
|
||||||
|
int32_t goertzel = g_goertzel;
|
||||||
|
int32_t accumulate_over_window = g_accumulate_over_window;
|
||||||
|
|
||||||
|
uint32_t goertzel_samples = g_goertzel_samples;
|
||||||
|
// Backup flags.
|
||||||
|
volatile int intfr = DMA1->INTFR;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// Clear all possible flags.
|
||||||
|
DMA1->INTFCR = DMA1_IT_GL1;
|
||||||
|
|
||||||
|
int tpl = ADC_BUFFSIZE - DMA1_Channel1->CNTR; // Warning, sometimes this is == to the base, or == 0 (i.e. might be 256, if top is 255)
|
||||||
|
tpl += ADC_BUFFSIZE;
|
||||||
|
tpl = (tpl & (ADC_BUFFSIZE-1));
|
||||||
|
if( tpl == ADC_BUFFSIZE ) tpl = 0;
|
||||||
|
|
||||||
|
adc_buffer_end = adc_buffer + ( ( tpl / 4) * 4 );
|
||||||
|
|
||||||
|
#define INFADC 2
|
||||||
|
// Add a tiny bias to the ADC to help keep goertz in range.
|
||||||
|
static int adc_offset = (-2048) << INFADC;
|
||||||
|
|
||||||
|
while( adc_tail != adc_buffer_end )
|
||||||
|
{
|
||||||
|
|
||||||
|
uint32_t t; // 1/2 of 4096, to try to keep our numbers reasonable.
|
||||||
|
|
||||||
|
// Here is where the magic happens.
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
#define XSTR(x) #x
|
||||||
|
#define GOERTZELLOOP(idx) \
|
||||||
|
asm volatile("\n\
|
||||||
|
lhu %[adcin]," XSTR(idx) "(%[adc_tail]) \n\
|
||||||
|
slli %[adcin],%[adcin],%[iadc] /*INFADC = 2*/ \n\
|
||||||
|
add %[adcin],%[adcin],%[adcoffset] /*adcin += adcoffset*/ \n\
|
||||||
|
add %[accumulate_over_window], %[adcin], %[accumulate_over_window]\n \
|
||||||
|
addi %[goertzelp2],%[goertzelp],0 /*goertzelp2 = goertzelp*/ \n\
|
||||||
|
addi %[goertzelp], %[goertzel],0 /*goertzelp = goertzel*/ \n\
|
||||||
|
slli %[goertzel], %[goertzelp], 2 /*prescaling up goertzelp*/\n\
|
||||||
|
mulh %[goertzel], %[goertzel_coefficient], %[goertzel]\n\
|
||||||
|
sub %[adcin],%[adcin],%[goertzelp2] /*adcin -= goertzelp2*/ \n\
|
||||||
|
add %[goertzel], %[goertzel], %[adcin] /* mulh = signed * signed + adc */ \n"\
|
||||||
|
: [goertzel]"+r"(goertzel), [goertzelp]"+r"(goertzelp), [goertzelp2]"+r"(goertzelp2), [adcin]"+r"(t), [accumulate_over_window]"+r"(accumulate_over_window) : \
|
||||||
|
[adc_tail]"r"(adc_tail), [adcoffset]"r"(adc_offset), [goertzel_coefficient]"r"(goertzel_coefficient), [iadc]"i"(INFADC) );
|
||||||
|
|
||||||
|
GOERTZELLOOP(0);
|
||||||
|
GOERTZELLOOP(2);
|
||||||
|
GOERTZELLOOP(4);
|
||||||
|
GOERTZELLOOP(6);
|
||||||
|
#else
|
||||||
|
t = ((adc_tail[0])<<INFADC)+adc_offset;
|
||||||
|
goertzelp2 = goertzelp;
|
||||||
|
goertzelp = goertzel;
|
||||||
|
goertzel = t + ( ( (((int32_t)(goertzel_coefficient))) * ((((int64_t)goertzelp)<<2)) ) >> 32 ) - goertzelp2;
|
||||||
|
|
||||||
|
t = ((adc_tail[1])<<INFADC)+adc_offset;
|
||||||
|
goertzelp2 = goertzelp;
|
||||||
|
goertzelp = goertzel;
|
||||||
|
goertzel = t + ( ( (((int32_t)(goertzel_coefficient))) * ((((int64_t)goertzelp)<<2)) ) >> 32 ) - goertzelp2;
|
||||||
|
|
||||||
|
t = ((adc_tail[2])<<INFADC)+adc_offset;
|
||||||
|
goertzelp2 = goertzelp;
|
||||||
|
goertzelp = goertzel;
|
||||||
|
goertzel = t + ( ( (((int32_t)(goertzel_coefficient))) * ((((int64_t)goertzelp)<<2)) ) >> 32 ) - goertzelp2;
|
||||||
|
|
||||||
|
t = ((adc_tail[3])<<INFADC)+adc_offset;
|
||||||
|
goertzelp2 = goertzelp;
|
||||||
|
goertzelp = goertzel;
|
||||||
|
goertzel = t + ( ( (((int32_t)(goertzel_coefficient))) * ((((int64_t)goertzelp)<<2)) ) >> 32 ) - goertzelp2;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
adc_tail+=4;
|
||||||
|
goertzel_samples+=4;
|
||||||
|
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_goertzelp2_store = goertzelp;
|
||||||
|
|
||||||
|
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] = ((uint16_t)rr) | (((uint16_t)ri)<<16);
|
||||||
|
qibaselogs_head = ( qibaselogs_head + 1 ) & ((LOG_GOERTZEL_LIST)-1);
|
||||||
|
|
||||||
|
rr>>=2;
|
||||||
|
ri>>=2;
|
||||||
|
|
||||||
|
int s = rr * rr + ri * ri;
|
||||||
|
//int intensity = 1<<( ( 32 - __builtin_clz(s) )/2);
|
||||||
|
#define ABS(x) (((x)<0)?-(x):(x))
|
||||||
|
int intensity = (ABS(rr) + ABS(ri)) * 26100 / 32768; // Found experimentally (Also try to avoid divide-by-zero.
|
||||||
|
if( intensity == 0 )
|
||||||
|
intensity = 1;
|
||||||
|
intensity = (intensity + s/intensity)/2;
|
||||||
|
intensity = (intensity + s/intensity)/2;
|
||||||
|
intensity_average = intensity_average - (intensity_average>>12) + (intensity>>6);
|
||||||
|
|
||||||
|
#ifdef PWM_OUTPUT
|
||||||
|
intensity = intensity * g_volume_pwm * g_pwm_period / (intensity_average>>(10-12));
|
||||||
|
if( intensity >= g_pwm_period-1 ) intensity = g_pwm_period-2;
|
||||||
|
if( intensity < 1 ) intensity = 1;
|
||||||
|
TIM1->CH2CVR = intensity; // Actual duty cycle (Off to begin with)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
g_goertzel_outs++;
|
||||||
|
goertzel = 0;
|
||||||
|
goertzelp = 0;
|
||||||
|
goertzel_samples = 0;
|
||||||
|
|
||||||
|
// Try to improve bias.
|
||||||
|
adc_offset -= accumulate_over_window / g_goertzel_buffer;
|
||||||
|
accumulate_over_window = 0;
|
||||||
|
|
||||||
|
#ifdef PROFILING_PIN
|
||||||
|
funDigitalWrite( PROFILING_PIN, 1 );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
intfr = DMA1->INTFR;
|
||||||
|
} while( intfr & DMA1_IT_GL1 );
|
||||||
|
|
||||||
|
g_goertzelp2 = goertzelp2;
|
||||||
|
g_goertzelp = goertzelp;
|
||||||
|
g_goertzel = goertzel;
|
||||||
|
g_goertzel_samples = goertzel_samples;
|
||||||
|
g_accumulate_over_window = accumulate_over_window;
|
||||||
|
|
||||||
|
#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()
|
||||||
|
{
|
||||||
|
uint32_t ret;
|
||||||
|
asm volatile( "mv %[ret], s2" : [ret]"=&r"(ret) );
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void InnerLoop()
|
||||||
|
{
|
||||||
|
while(1){
|
||||||
|
#if 0
|
||||||
|
int adcz = adc_buffer[0];
|
||||||
|
for( k = 0; k < 128; k++ )
|
||||||
|
{
|
||||||
|
int y = adc_buffer[k]-adcz + 64;
|
||||||
|
if( y < 0 ) y = 0;
|
||||||
|
if( y > 127 ) y = 127;
|
||||||
|
ssd1306_drawPixel( k, y, 1 );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int pxa = 0;
|
||||||
|
|
||||||
|
// Only display half of the list so the other half could
|
||||||
|
// be updated by the ISR.
|
||||||
|
int glread = qibaselogs_head;
|
||||||
|
|
||||||
|
int intensity = 0;
|
||||||
|
|
||||||
|
for( pxa = 0; pxa < LOG_GOERTZEL_LIST; pxa++ )
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
ri = ri * 512 / (intensity_average);
|
||||||
|
|
||||||
|
rr += 64;
|
||||||
|
ri += 64;
|
||||||
|
|
||||||
|
if( rr < 0 ) rr = 0;
|
||||||
|
if( ri < 0 ) ri = 0;
|
||||||
|
if( rr > 127 ) rr = 127;
|
||||||
|
if( ri > 127 ) ri = 127;
|
||||||
|
|
||||||
|
#ifdef ENABLE_OLED
|
||||||
|
ssd1306_drawPixel( rr, ri, 1 );
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ENABLE_OLED
|
||||||
|
//char cts[32];
|
||||||
|
//snprintf( cts, 32, "%d", intensity_average );
|
||||||
|
//ssd1306_drawstr( 0, 0, cts, 1 );
|
||||||
|
|
||||||
|
ssd1306_refresh();
|
||||||
|
//static int ik = 0;
|
||||||
|
//printf( "%d %08x\n", ik, ssd1306_buffer[ik++] );
|
||||||
|
//if( ik == sizeof(ssd1306_buffer) ) ik = 0;
|
||||||
|
|
||||||
|
ssd1306_setbuf(0);
|
||||||
|
|
||||||
|
#else
|
||||||
|
Delay_Ms(17);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// printf( "%6d %8d %8d - %8d %8d - %8d\n", g_goertzel_outs,g_goertzelp2_store, g_goertzelp_store, rr, ri, x );
|
||||||
|
|
||||||
|
// Delay_Ms(940);
|
||||||
|
//printf( "!!!!\n ");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
SystemInit();
|
||||||
|
|
||||||
|
SysTick->CTLR = (1<<2) | 1; // HCLK
|
||||||
|
Delay_Ms(100);
|
||||||
|
|
||||||
|
RCC->CTLR |= RCC_HSEON;
|
||||||
|
while( ! ( RCC->CTLR & RCC_HSERDY ) );
|
||||||
|
|
||||||
|
RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_HSE;
|
||||||
|
|
||||||
|
RCC->CTLR &= ~RCC_PLLON;
|
||||||
|
|
||||||
|
// Switch PLL to HSE.
|
||||||
|
RCC->CFGR0 |= RCC_PLLSRC;
|
||||||
|
|
||||||
|
// x18; 8MHz x 18 = 144 MHz
|
||||||
|
RCC->CFGR0 &= ~RCC_PPRE2; // No divisor on APB1/2
|
||||||
|
RCC->CFGR0 &= ~RCC_PPRE1;
|
||||||
|
RCC->CFGR0 |= RCC_PLLMULL_0 | RCC_PLLMULL_1 | RCC_PLLMULL_2 | RCC_PLLMULL_3;
|
||||||
|
|
||||||
|
RCC->CTLR |= RCC_PLLON;
|
||||||
|
|
||||||
|
// Switch to PLL
|
||||||
|
RCC->CFGR0 = (RCC->CFGR0 & ~RCC_SW) | RCC_SW_PLL;
|
||||||
|
|
||||||
|
// Disable HSI
|
||||||
|
RCC->CTLR &= ~(RCC_HSION);
|
||||||
|
|
||||||
|
Delay_Ms(10);
|
||||||
|
|
||||||
|
|
||||||
|
//printf( "CTLR: %08x / CFGR0: %08x\n", (RCC->CTLR), (RCC->CFGR0) );
|
||||||
|
RCC->AHBPCENR |= 3; //DMA2EN | DMA1EN
|
||||||
|
RCC->APB2PCENR |= RCC_APB2Periph_TIM1 | RCC_APB2Periph_ADC1 | RCC_APB2Periph_ADC2 | 0x07 | RCC_APB2Periph_GPIOA; // Enable all GPIO
|
||||||
|
RCC->APB1PCENR |= RCC_APB1Periph_TIM2;
|
||||||
|
|
||||||
|
SetupADC();
|
||||||
|
|
||||||
|
#ifdef ENABLE_OLED
|
||||||
|
ssd1306_i2c_setup();
|
||||||
|
ssd1306_i2c_init();
|
||||||
|
|
||||||
|
if( ssd1306_init() )
|
||||||
|
printf( "Failed to initialize OLED\n" );
|
||||||
|
else
|
||||||
|
printf( "Initialized OLED\n" );
|
||||||
|
|
||||||
|
ssd1306_setbuf(1);
|
||||||
|
ssd1306_refresh();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
int i = 0;
|
||||||
|
int k = 0;
|
||||||
|
int frame = 0;
|
||||||
|
while( 1)
|
||||||
|
{
|
||||||
|
// ssd1306_drawLine( (frame)%128, (0)%128, (0)%128, (127-frame)%128, 1 );
|
||||||
|
ssd1306_drawstr( frame%128, frame%128, "hello", 1 );
|
||||||
|
|
||||||
|
ssd1306_refresh();
|
||||||
|
ssd1306_setbuf(0);
|
||||||
|
frame++;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
// turn on the op-amp
|
||||||
|
EXTEN->EXTEN_CTR |= EXTEN_OPA_EN;
|
||||||
|
|
||||||
|
// select op-amp pos pin: 0 = PA2, 1 = PD7
|
||||||
|
EXTEN->EXTEN_CTR |= EXTEN_OPA_PSEL;
|
||||||
|
|
||||||
|
// select op-amp neg pin: 0 = PA1, 1 = PD0
|
||||||
|
EXTEN->EXTEN_CTR |= EXTEN_OPA_NSEL;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PROFILING_PIN
|
||||||
|
funPinMode( PROFILING_PIN, GPIO_CFGLR_OUT_50Mhz_PP );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SetupTimer1();
|
||||||
|
|
||||||
|
USBOTGSetup();
|
||||||
|
|
||||||
|
|
||||||
|
InnerLoop();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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 ) )
|
||||||
|
{
|
||||||
|
ctx->pCtrlPayloadPtr = scratchpad;
|
||||||
|
return req->wLength;
|
||||||
|
}
|
||||||
|
else if( id == 0xac && req->wLength <= sizeof( scratchpad ) )
|
||||||
|
{
|
||||||
|
g_isConfigurePacket = 1;
|
||||||
|
ctx->pCtrlPayloadPtr = scratchpad;
|
||||||
|
return req->wLength;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HandleHidUserGetReportSetup( struct _USBState * ctx, tusb_control_request_t * req )
|
||||||
|
{
|
||||||
|
int id = req->wValue & 0xff;
|
||||||
|
if( id == 0xaa )
|
||||||
|
{
|
||||||
|
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<<12) | samps_to_send;
|
||||||
|
((uint32_t*)scratchpad)[1] = (g_lastper<<16) | g_lastlen;
|
||||||
|
((uint32_t*)scratchpad)[2] = (0<<16) | (((g_pwm_period+1)*g_goertzel_buffer)); //LSW = 144MHz / X
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleHidUserReportDataOut( struct _USBState * ctx, uint8_t * data, int len )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int HandleHidUserReportDataIn( struct _USBState * ctx, uint8_t * data, int len )
|
||||||
|
{
|
||||||
|
// printf( "IN %d %d %08x %08x\n", len, ctx->USBFS_SetupReqLen, data, FSUSBCTX.ENDPOINTS[0] );
|
||||||
|
// memset( data, 0xcc, len );
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleHidUserReportOutComplete( struct _USBState * ctx )
|
||||||
|
{
|
||||||
|
if( g_isConfigurePacket )
|
||||||
|
{
|
||||||
|
|
||||||
|
uint32_t * configs = (uint32_t*)scratchpad;
|
||||||
|
// Note: configs[0] == 0xac (command type)
|
||||||
|
|
||||||
|
printf( "Is Configure Packet %08x\n", configs[1] );
|
||||||
|
|
||||||
|
int numconfigs = configs[1];
|
||||||
|
if( numconfigs > 0) g_pwm_period = configs[2];
|
||||||
|
if( numconfigs > 1) g_goertzel_buffer = configs[3];
|
||||||
|
if( numconfigs > 2) g_goertzel_omega_per_sample = configs[4]; // 0.816667 of whole per step / 0.880000MHz
|
||||||
|
if( numconfigs > 3) g_goertzel_coefficient = configs[5];
|
||||||
|
if( numconfigs > 4) g_goertzel_coefficient_s = configs[6];
|
||||||
|
if( numconfigs > 5) g_exactcompute = configs[7];
|
||||||
|
|
||||||
|
// Need to reset so we don't blast by.
|
||||||
|
g_goertzel_samples = 0;
|
||||||
|
TIM1->ATRLR = g_pwm_period;
|
||||||
|
|
||||||
|
g_isConfigurePacket = 0;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#ifndef _FUNCONFIG_H
|
||||||
|
#define _FUNCONFIG_H
|
||||||
|
|
||||||
|
#define FUNCONF_USE_DEBUGPRINTF 1
|
||||||
|
#define FUNCONF_USE_UARTPRINTF 0
|
||||||
|
#define FUNCONF_USE_HSE 1
|
||||||
|
#define FUNCONF_SYSTICK_USE_HCLK 1
|
||||||
|
#define FUNCONF_ENABLE_HPE 0
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
all : test floattest
|
||||||
|
|
||||||
|
floattest : floattestt
|
||||||
|
./floattestt
|
||||||
|
|
||||||
|
floattestt : floattestt.c
|
||||||
|
gcc -o $@ $^ -lm -g
|
||||||
|
|
||||||
|
|
||||||
|
test : ttest
|
||||||
|
./ttest
|
||||||
|
|
||||||
|
ttest : ttest.c
|
||||||
|
gcc -o $@ $^ -lm -g
|
||||||
|
|
||||||
|
clean :
|
||||||
|
rm -rf *.o ttest *~ floattestt
|
||||||
Binary file not shown.
@@ -0,0 +1,112 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
uint32_t g_goertzel_omega_per_sample;
|
||||||
|
int32_t g_goertzel_coefficient;
|
||||||
|
int32_t g_goertzel_coefficient_s;
|
||||||
|
uint32_t g_goertzel_samples;
|
||||||
|
uint32_t g_goertzel_outs;
|
||||||
|
int32_t g_goertzelp, g_goertzelp2;
|
||||||
|
int32_t g_goertzelp_store, g_goertzelp2_store;
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
//XXX XXX NOTE If you are computing the coefficients, you can plug the value in here.
|
||||||
|
// You can get this number from calculator.html. I've had better luck with "inverse goertzel" numbers.
|
||||||
|
g_goertzel_omega_per_sample = (47.0/256) * 3.1415926535*2.0*(1<<29);
|
||||||
|
g_goertzel_coefficient = 2 * cos( g_goertzel_omega_per_sample / (double)(1<<29) ) * (1<<30);
|
||||||
|
g_goertzel_coefficient_s = 2 * sin( g_goertzel_omega_per_sample / (double)(1<<29) ) * (1<<30);
|
||||||
|
|
||||||
|
|
||||||
|
const double AomegaPerSample = g_goertzel_omega_per_sample/(double)(1<<29);
|
||||||
|
const int AnumSamples = 256; // enough to go from 0 to 2pi
|
||||||
|
double Acoeff = 2 * cos( AomegaPerSample ) * 65536;
|
||||||
|
double Acoeff_s = 2 * sin( AomegaPerSample ) * 65536;
|
||||||
|
double Asprev = AomegaPerSample * 65536;
|
||||||
|
double Asprev2 = 0;
|
||||||
|
|
||||||
|
printf( "%d / %d / %d %f / %f / %f\n", g_goertzel_omega_per_sample, g_goertzel_coefficient, g_goertzel_coefficient_s, Acoeff, Acoeff_s, AomegaPerSample );
|
||||||
|
|
||||||
|
|
||||||
|
g_goertzelp = g_goertzel_omega_per_sample>>(29-16);
|
||||||
|
|
||||||
|
int32_t goertzel_coefficient = g_goertzel_coefficient;
|
||||||
|
int32_t goertzelp2 = g_goertzelp2;
|
||||||
|
int32_t goertzelp = g_goertzelp;
|
||||||
|
uint32_t goertzel_samples = g_goertzel_samples;
|
||||||
|
|
||||||
|
uint32_t adc_tail;
|
||||||
|
|
||||||
|
double As;
|
||||||
|
|
||||||
|
int js = 0;
|
||||||
|
for( js = 0; js < 260/4; js++ )
|
||||||
|
{
|
||||||
|
int32_t t; // 1/2 of 4096, to try to keep our numbers reasonable.
|
||||||
|
|
||||||
|
// Here is where the magic happens.
|
||||||
|
int32_t goertzel;
|
||||||
|
|
||||||
|
#define ITERATION(x) \
|
||||||
|
t = sin( AomegaPerSample * (x+js*4) ) * 16384*0; \
|
||||||
|
\
|
||||||
|
/* Fixed */ \
|
||||||
|
goertzelp2 = goertzelp; \
|
||||||
|
goertzelp = goertzel; \
|
||||||
|
goertzel = t + ( ( (((int32_t)(goertzel_coefficient))) * ((((int64_t)goertzelp)<<2)) ) >> 32 ) - goertzelp2; \
|
||||||
|
\
|
||||||
|
/* Float */ \
|
||||||
|
Asprev2 = Asprev; \
|
||||||
|
Asprev = As; \
|
||||||
|
As = t + ( Acoeff * Asprev ) / 65536.0 - Asprev2; \
|
||||||
|
\
|
||||||
|
/*printf( "%d,%d,%d,%d\n", ( ( (int64_t)(goertzel_coefficient) * (int64_t)goertzelp ) >> 32 ) , goertzel_coefficient, goertzelp, goertzelp2 ); */ \
|
||||||
|
\
|
||||||
|
{\
|
||||||
|
int32_t rr = (((int64_t)(g_goertzel_coefficient ) * (int64_t)goertzelp<<1)>>32) - (goertzelp2); \
|
||||||
|
int32_t ri = (((int64_t)(g_goertzel_coefficient_s) * (int64_t)goertzelp<<1)>>32); \
|
||||||
|
/*printf( "%3d %10d %10d %10d %10d / %9d %9d %9d * ", js*4+x, rr, ri, goertzelp, goertzelp2, goertzel_coefficient, g_goertzel_omega_per_sample, t );*/ \
|
||||||
|
/*printf( "%4d %10d %10d (%10d %10d) ", js*4+x, goertzelp, goertzelp2, goertzel_coefficient, g_goertzel_coefficient_s );*/ \
|
||||||
|
printf( "%4d %10d %10d (%10d %10d) ", js*4+x, goertzelp, goertzelp2, rr, ri ); \
|
||||||
|
\
|
||||||
|
float Apower = Asprev*Asprev + Asprev2*Asprev2 - (Acoeff * Asprev * Asprev2); \
|
||||||
|
double ArR = 0.5 * Acoeff * Asprev / 65536 - Asprev2; \
|
||||||
|
double ArI = 0.5 * Acoeff_s * Asprev / 65536; \
|
||||||
|
/*printf( "%14.3f, %14.3f (%14.3f %14.3f)\n", Asprev, Asprev2, Acoeff,Acoeff_s );*/ \
|
||||||
|
printf( "%14.3f, %14.3f (%14.3f %14.3f %f)\n", Asprev, Asprev2, ArR, ArI, sqrt( ArR*ArR +ArI*ArI ) ); \
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ITERATION( 0 );
|
||||||
|
ITERATION( 1 );
|
||||||
|
ITERATION( 2 );
|
||||||
|
ITERATION( 3 );
|
||||||
|
|
||||||
|
adc_tail+=4;
|
||||||
|
goertzel_samples+=4;
|
||||||
|
// if( adc_tail == adc_buffer_top ) adc_tail = adc_buffer;
|
||||||
|
|
||||||
|
/* if( goertzel_samples == 128 )
|
||||||
|
{
|
||||||
|
g_goertzelp_store = goertzelp;
|
||||||
|
g_goertzelp2_store = goertzelp2;
|
||||||
|
|
||||||
|
goertzelp = 0;
|
||||||
|
goertzelp2 = 0;
|
||||||
|
|
||||||
|
g_goertzel_outs++;
|
||||||
|
goertzel_samples = 0;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
g_goertzelp2 = goertzelp2;
|
||||||
|
g_goertzelp = goertzelp;
|
||||||
|
g_goertzel_samples = goertzel_samples;
|
||||||
|
|
||||||
|
printf( "Constants:\nconst int32_t g_goertzel_omega_per_sample = %d;\nconst int32_t g_goertzel_coefficient = %d;\nconst int32_t g_goertzel_coefficient_s = %d;\n", g_goertzel_omega_per_sample, g_goertzel_coefficient, g_goertzel_coefficient_s );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,157 @@
|
|||||||
|
#ifndef _USB_CONFIG_H
|
||||||
|
#define _USB_CONFIG_H
|
||||||
|
|
||||||
|
#include "funconfig.h"
|
||||||
|
#include "ch32v003fun.h"
|
||||||
|
|
||||||
|
#define FUSB_CONFIG_EPS 2 // Include EP0 in this count
|
||||||
|
#define FUSB_SUPPORTS_SLEEP 0
|
||||||
|
#define FUSB_HID_INTERFACES 1
|
||||||
|
#define FUSB_CURSED_TURBO_DMA 0 // Hacky, but seems fine, shaves 2.5us off filling 64-byte buffers.
|
||||||
|
#define FUSB_HID_USER_REPORTS 1
|
||||||
|
#define FUSB_IO_PROFILE 1
|
||||||
|
#define FUSB_USE_HPE FUNCONF_ENABLE_HPE
|
||||||
|
|
||||||
|
#include "usb_defines.h"
|
||||||
|
|
||||||
|
//Taken from http://www.usbmadesimple.co.uk/ums_ms_desc_dev.htm
|
||||||
|
static const uint8_t device_descriptor[] = {
|
||||||
|
18, //Length
|
||||||
|
1, //Type (Device)
|
||||||
|
0x00, 0x02, //Spec
|
||||||
|
0x0, //Device Class
|
||||||
|
0x0, //Device Subclass
|
||||||
|
0x0, //Device Protocol (000 = use config descriptor)
|
||||||
|
64, //Max packet size for EP0 (This has to be 8 because of the USB Low-Speed Standard)
|
||||||
|
0x09, 0x12, //ID Vendor
|
||||||
|
0x35, 0xd0, //ID Product
|
||||||
|
0x03, 0x00, //ID Rev
|
||||||
|
1, //Manufacturer string
|
||||||
|
2, //Product string
|
||||||
|
3, //Serial string
|
||||||
|
1, //Max number of configurations
|
||||||
|
};
|
||||||
|
|
||||||
|
static const uint8_t HIDAPIRepDesc[ ] =
|
||||||
|
{
|
||||||
|
HID_USAGE_PAGE ( 0xff ), // Vendor-defined page.
|
||||||
|
HID_USAGE ( 0x00 ),
|
||||||
|
HID_REPORT_SIZE ( 8 ),
|
||||||
|
HID_COLLECTION ( HID_COLLECTION_LOGICAL ),
|
||||||
|
HID_REPORT_COUNT ( 254 ),
|
||||||
|
HID_REPORT_ID ( 0xaa )
|
||||||
|
HID_USAGE ( 0x01 ),
|
||||||
|
HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,
|
||||||
|
HID_REPORT_COUNT ( 63 ), // For use with `hidapitester --vidpid 1209/D003 --open --read-feature 171`
|
||||||
|
HID_REPORT_ID ( 0xab )
|
||||||
|
HID_USAGE ( 0x01 ),
|
||||||
|
HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,
|
||||||
|
HID_REPORT_COUNT ( 63 ), // For configuring the setup.
|
||||||
|
HID_REPORT_ID ( 0xac )
|
||||||
|
HID_USAGE ( 0x01 ),
|
||||||
|
HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) ,
|
||||||
|
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 ) ,
|
||||||
|
HID_COLLECTION_END,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Configuration Descriptor Set */
|
||||||
|
static const uint8_t config_descriptor[ ] =
|
||||||
|
{
|
||||||
|
/* Configuration Descriptor */
|
||||||
|
0x09, // bLength
|
||||||
|
0x02, // bDescriptorType
|
||||||
|
0x22, 0x00, // wTotalLength
|
||||||
|
0x01, // bNumInterfaces (3)
|
||||||
|
0x01, // bConfigurationValue
|
||||||
|
0x00, // iConfiguration
|
||||||
|
0xA0, // bmAttributes: Bus Powered; Remote Wakeup
|
||||||
|
0x32, // MaxPower: 100mA
|
||||||
|
|
||||||
|
/* Interface Descriptor (Special) */
|
||||||
|
0x09, // bLength
|
||||||
|
0x04, // bDescriptorType
|
||||||
|
0x00, // bInterfaceNumber
|
||||||
|
0x01, // bAlternateSetting
|
||||||
|
0x01, // bNumEndpoints
|
||||||
|
0x03, // bInterfaceClass
|
||||||
|
0x00, // bInterfaceSubClass
|
||||||
|
0xff, // bInterfaceProtocol: OTher
|
||||||
|
0x00, // iInterface
|
||||||
|
|
||||||
|
/* HID Descriptor (Special) */
|
||||||
|
0x09, // bLength
|
||||||
|
0x21, // bDescriptorType
|
||||||
|
0x10, 0x01, // bcdHID
|
||||||
|
0x00, // bCountryCode
|
||||||
|
0x01, // bNumDescriptors
|
||||||
|
0x22, // bDescriptorType
|
||||||
|
sizeof(HIDAPIRepDesc), 0x00, // wDescriptorLength
|
||||||
|
|
||||||
|
/* Endpoint Descriptor (Special) */
|
||||||
|
0x07, // bLength
|
||||||
|
0x05, // bDescriptorType
|
||||||
|
0x81, // bEndpointAddress: IN Endpoint 1
|
||||||
|
0x03, // bmAttributes
|
||||||
|
0x08, 0x00, // wMaxPacketSize
|
||||||
|
0xff, // bInterval: slow.
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define STR_MANUFACTURER u"CNLohr"
|
||||||
|
#define STR_PRODUCT u"lolra ch32v203 goertzel test"
|
||||||
|
#define STR_SERIAL u"CUSTOMDEVICE000"
|
||||||
|
|
||||||
|
struct usb_string_descriptor_struct {
|
||||||
|
uint8_t bLength;
|
||||||
|
uint8_t bDescriptorType;
|
||||||
|
uint16_t wString[];
|
||||||
|
};
|
||||||
|
const static struct usb_string_descriptor_struct string0 __attribute__((section(".rodata"))) = {
|
||||||
|
4,
|
||||||
|
3,
|
||||||
|
{0x0409}
|
||||||
|
};
|
||||||
|
const static struct usb_string_descriptor_struct string1 __attribute__((section(".rodata"))) = {
|
||||||
|
sizeof(STR_MANUFACTURER),
|
||||||
|
3,
|
||||||
|
STR_MANUFACTURER
|
||||||
|
};
|
||||||
|
const static struct usb_string_descriptor_struct string2 __attribute__((section(".rodata"))) = {
|
||||||
|
sizeof(STR_PRODUCT),
|
||||||
|
3,
|
||||||
|
STR_PRODUCT
|
||||||
|
};
|
||||||
|
const static struct usb_string_descriptor_struct string3 __attribute__((section(".rodata"))) = {
|
||||||
|
sizeof(STR_SERIAL),
|
||||||
|
3,
|
||||||
|
STR_SERIAL
|
||||||
|
};
|
||||||
|
|
||||||
|
// This table defines which descriptor data is sent for each specific
|
||||||
|
// request from the host (in wValue and wIndex).
|
||||||
|
const static struct descriptor_list_struct {
|
||||||
|
uint32_t lIndexValue;
|
||||||
|
const uint8_t *addr;
|
||||||
|
uint8_t length;
|
||||||
|
} descriptor_list[] = {
|
||||||
|
{0x00000100, device_descriptor, sizeof(device_descriptor)},
|
||||||
|
{0x00000200, config_descriptor, sizeof(config_descriptor)},
|
||||||
|
// interface number // 2200 for hid descriptors.
|
||||||
|
{0x00002200, HIDAPIRepDesc, sizeof(HIDAPIRepDesc)},
|
||||||
|
|
||||||
|
{0x00002100, config_descriptor + 18, 9 }, // Not sure why, this seems to be useful for Windows + Android.
|
||||||
|
|
||||||
|
{0x00000300, (const uint8_t *)&string0, 4},
|
||||||
|
{0x04090301, (const uint8_t *)&string1, sizeof(STR_MANUFACTURER)},
|
||||||
|
{0x04090302, (const uint8_t *)&string2, sizeof(STR_PRODUCT)},
|
||||||
|
{0x04090303, (const uint8_t *)&string3, sizeof(STR_SERIAL)}
|
||||||
|
};
|
||||||
|
#define DESCRIPTOR_LIST_ENTRIES ((sizeof(descriptor_list))/(sizeof(struct descriptor_list_struct)) )
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
Binary file not shown.
@@ -0,0 +1,315 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<HTML>
|
||||||
|
<HEAD>
|
||||||
|
<LINK rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
|
||||||
|
<META charset="UTF-8">
|
||||||
|
<STYLE>
|
||||||
|
body { background-color: Canvas; color: CanvasText; }
|
||||||
|
:root { color-scheme: dark; }
|
||||||
|
</STYLE>
|
||||||
|
|
||||||
|
<SCRIPT src=webhidcontrol.js></SCRIPT>
|
||||||
|
|
||||||
|
<SCRIPT>
|
||||||
|
function DrawSpan( rowspan, colspan, freq, target, docolor, extrastr = "" )
|
||||||
|
{
|
||||||
|
var fdist = Math.abs( freq - target );
|
||||||
|
fdist = Math.pow( fdist, 0.5 ) * 500;
|
||||||
|
// if( fdist > 255 ) fdist = 255;
|
||||||
|
let ret = "<TD COLSPAN=" + colspan + ' ROWSPAN' + rowspan + ' ';
|
||||||
|
if( docolor ) ret += 'STYLE="color:black;background-color:rgb(' + fdist + ',' + (511-fdist) + ',0)";';
|
||||||
|
ret += '>' + extrastr + freq.toFixed(6) + "</TD>";
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Goertz( n, mhz, fr, brf, exact_compute )
|
||||||
|
{
|
||||||
|
let omega = fr * 3.1415926535*2.0;
|
||||||
|
var textarea = document.getElementById("goertzeloutput");
|
||||||
|
|
||||||
|
var g_goertzel_omega_per_sample = Math.round( ( omega*2*(1<<29)) );
|
||||||
|
var g_goertzel_coefficient = Math.round( (2 * Math.cos( omega ) * (1<<30)) );
|
||||||
|
var g_goertzel_coefficient_s = Math.round( (2 * Math.sin( omega ) * (1<<30)) );
|
||||||
|
var g_exactcompute = exact_compute;
|
||||||
|
textarea.value =
|
||||||
|
"int g_pwm_period = ("+n+"-1);\n" +
|
||||||
|
"int g_exactcompute = ("+exact_compute+");\n" +
|
||||||
|
"int g_goertzel_buffer = ("+brf+");\n" +
|
||||||
|
"int32_t g_goertzel_omega_per_sample = " + g_goertzel_coefficient.toFixed(0) + "; // " + ( omega / (3.1415926535*2.0)).toFixed(6) + " of whole per step / " + mhz.toFixed(6) + "MHz\n" +
|
||||||
|
"int32_t g_goertzel_coefficient = " + g_goertzel_coefficient.toFixed(0) + ";\n" +
|
||||||
|
"int32_t g_goertzel_coefficient_s = " + g_goertzel_coefficient_s.toFixed(0) + ";\n";
|
||||||
|
|
||||||
|
// Highlight its content
|
||||||
|
textarea.select();
|
||||||
|
|
||||||
|
// Copy the highlighted text
|
||||||
|
document.execCommand("copy");
|
||||||
|
|
||||||
|
updateWebHidDeviceWithParameters( [ (n-1)|0, brf|0, g_goertzel_omega_per_sample|0, g_goertzel_coefficient|0, g_goertzel_coefficient_s|0, exact_compute|0 ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
function computeTable()
|
||||||
|
{
|
||||||
|
let xtal = Number(document.getElementById("crystalmhz").value );
|
||||||
|
let target = Number(document.getElementById("targetmhz").value );
|
||||||
|
let quadrature = document.getElementById("QUADRATURE").checked;
|
||||||
|
let goertzels = document.getElementById("GOERTZELS").checked;
|
||||||
|
let goertzel2 = document.getElementById("GOERTZEL2").checked;
|
||||||
|
let quanta = Math.round(Number(document.getElementById("quanta").value));
|
||||||
|
let quantasearch = Math.round(Number(document.getElementById("quantasearch").value));
|
||||||
|
|
||||||
|
|
||||||
|
const max_harmonics = 28|0;
|
||||||
|
const min_harmonics = (quadrature?1:0)|0;
|
||||||
|
|
||||||
|
let contents = "";
|
||||||
|
|
||||||
|
if( quadrature )
|
||||||
|
{
|
||||||
|
contents += "<TABLE>" +
|
||||||
|
"<TR><TD>Quadrature:</TD></TR>" +
|
||||||
|
"<TR><TD>I = + + - -</TD></TR>" +
|
||||||
|
"<TR><TD>Q = + - - +</TD></TR>" +
|
||||||
|
"<TR><TD>Differntial:</TD></TR>" +
|
||||||
|
"<TR><TD>V = + - + -</TD></TR>" +
|
||||||
|
"<TR><TD>You choose the mode you operate in, either Quadrature or differential</TD></TR>" +
|
||||||
|
"</TABLE>" +
|
||||||
|
"<p> Table shows: </P>" +
|
||||||
|
"<TABLE BORDER=1>" +
|
||||||
|
"<TR><TD>Sample Frequency Harmonic</TD></TR>" +
|
||||||
|
"<TR><TD>Lower Quadrature Frequency</TD></TR>" +
|
||||||
|
"<TR><TD>Upper Quadrature Frequency</TD></TR>" +
|
||||||
|
"<TR><TD>Differential Frequency</TD></TR>" +
|
||||||
|
"</TABLE>";
|
||||||
|
}
|
||||||
|
else if( goertzels )
|
||||||
|
{
|
||||||
|
contents +=
|
||||||
|
"<TABLE BORDER=1>" +
|
||||||
|
"<TR><TD>Goertzel</TD></TR>" +
|
||||||
|
"<TR><TD>Goertzel (Inverse)</TD></TR>" +
|
||||||
|
"</TABLE><TEXTAREA ROWS=6 COLS=120 ID=goertzeloutput></TEXTAREA>" +
|
||||||
|
"<P>Click on a ordinal offset to create the C code needed for that tuning parameter. Clicking will copy-to-clipboard.</P>" +
|
||||||
|
"<P>N Divisor #30 (row 3) is usually pretty good. And, try to select things near 0.25 / 0.75, and avoid 0.0, 0.5, and 1.0.</P>" +
|
||||||
|
"<P>Goertzel's mode is for the ch32v203</P>";
|
||||||
|
}
|
||||||
|
|
||||||
|
if( goertzels || quadrature )
|
||||||
|
{
|
||||||
|
contents += "<TABLE BORDER=1>";
|
||||||
|
contents += '<TR><TH>d\\h</div></TH>';
|
||||||
|
for( let h = 0|min_harmonics; h <= max_harmonics; h++ )
|
||||||
|
{
|
||||||
|
contents += "<TH COLSPAN=2>" + h + "</TH>";
|
||||||
|
}
|
||||||
|
|
||||||
|
for( let n = 0|28; n <= 66; n++ )
|
||||||
|
{
|
||||||
|
let freq = ( xtal / n );
|
||||||
|
let goertzelpoint = 0;
|
||||||
|
let goertzelpointinv = 0;
|
||||||
|
let tgoertzelp = 0;
|
||||||
|
let tgoertzelpi = 0;
|
||||||
|
let quantaA = 0;
|
||||||
|
let quantaINV = 0;
|
||||||
|
for( let h = 0|min_harmonics; h <= max_harmonics; h++ )
|
||||||
|
{
|
||||||
|
let base = freq * h;
|
||||||
|
let next = freq * (h+1);
|
||||||
|
|
||||||
|
// Round quanta down to next order-of-4-even
|
||||||
|
for( let tquanta = (quanta&0xffffc) - (quantasearch&0xffffc); tquanta < (quanta&0xffffc) + (quantasearch&0xffffc); tquanta+=4 )
|
||||||
|
{
|
||||||
|
if( target <= next && target >= base )
|
||||||
|
{
|
||||||
|
var t;
|
||||||
|
let tgoertzelpoint = ( target - base ) / ( next - base );
|
||||||
|
tgoertzelpoint = Math.round(tquanta * tgoertzelpoint)/tquanta;
|
||||||
|
if( Math.abs( freq*(h+tgoertzelpoint) - target ) < Math.abs( freq*(h+goertzelpoint) - target ) )
|
||||||
|
{
|
||||||
|
goertzelpoint = tgoertzelpoint;
|
||||||
|
tgoertzelp = h;
|
||||||
|
quantaA = tquanta;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tgoertzelpointinv = (1.0 - ( target - base ) / ( next - base ));
|
||||||
|
tgoertzelpointinv = Math.round(tquanta * tgoertzelpointinv)/tquanta;
|
||||||
|
|
||||||
|
if( Math.abs( freq*(h-tgoertzelpointinv+1) - target ) < Math.abs( freq*(h-goertzelpointinv+1) - target ) )
|
||||||
|
{
|
||||||
|
goertzelpointinv = tgoertzelpointinv;
|
||||||
|
tgoertzelpi = h;
|
||||||
|
quantaINV = tquanta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contents += "</TR>";
|
||||||
|
|
||||||
|
for( let mode = 0; mode < 4; mode++ )
|
||||||
|
{
|
||||||
|
contents += "<TR>";
|
||||||
|
if( mode == 0 )
|
||||||
|
contents += "<TD ROWSPAN=" + 4 + ">" + n + "</TD>";
|
||||||
|
for( let h = 0|min_harmonics; h <= max_harmonics; h++ )
|
||||||
|
{
|
||||||
|
if( quadrature )
|
||||||
|
{
|
||||||
|
if( mode == 0 )
|
||||||
|
contents += DrawSpan( 1, 2, freq * h, target, false );
|
||||||
|
else if( mode == 1 )
|
||||||
|
contents += DrawSpan( 1, 2, freq * (h-.25), target, true );
|
||||||
|
else if( mode == 2 )
|
||||||
|
contents += DrawSpan( 1, 2, freq * (h+.25), target, true );
|
||||||
|
else if( mode == 3 )
|
||||||
|
contents += DrawSpan( 1, 2, freq * (h+0.5), target, true );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if( mode == 0 )
|
||||||
|
{
|
||||||
|
contents += "<TD COLSPAN=2>"
|
||||||
|
if( tgoertzelp == h ) contents += "<SPAN ONCLICK='Goertz(" + n + ", " + freq * (h+goertzelpoint) + ", " + (goertzelpoint) + ", " + quantaA + ", 0)'>↑" + (goertzelpoint).toFixed(6) + "</SPAN>";
|
||||||
|
contents += "</TD>";
|
||||||
|
}
|
||||||
|
else if( mode == 1 )
|
||||||
|
{
|
||||||
|
contents += DrawSpan( 1, 2, freq * (h+goertzelpoint), target, true );
|
||||||
|
}
|
||||||
|
else if( mode == 2 )
|
||||||
|
{
|
||||||
|
contents += "<TD COLSPAN=2>"
|
||||||
|
if( tgoertzelpi == h-1 ) contents += "<SPAN ONCLICK='Goertz(" + n + ", " + freq * (h-goertzelpointinv) + ", " + goertzelpointinv + ", " + quantaINV + ", 0)'>↓" + goertzelpointinv.toFixed(6) + "</SPAN>";
|
||||||
|
contents += "</TD>";
|
||||||
|
}
|
||||||
|
else if( mode == 3 )
|
||||||
|
{
|
||||||
|
contents += DrawSpan( 1, 2, freq * (h-goertzelpointinv), target, true );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contents += "</TD>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contents += "</TABLE>";
|
||||||
|
}
|
||||||
|
else if( goertzel2 )
|
||||||
|
{
|
||||||
|
contents += "</TABLE><TEXTAREA ROWS=6 COLS=120 ID=goertzeloutput></TEXTAREA><BR>";
|
||||||
|
contents += "<TABLE BORDER=1>";
|
||||||
|
contents += '<TR><TH>d\\h</div></TH>';
|
||||||
|
for( let h = 0|min_harmonics; h <= max_harmonics+1; h++ )
|
||||||
|
{
|
||||||
|
contents += "<TH COLSPAN=1>" + h + "</TH>";
|
||||||
|
}
|
||||||
|
|
||||||
|
for( let n = 0|28; n <= 96; n++ )
|
||||||
|
{
|
||||||
|
let freq = ( xtal / n );
|
||||||
|
let goertzelpoint = 0;
|
||||||
|
let goertzelpointinv = 0;
|
||||||
|
let tgoertzelp = 0;
|
||||||
|
let tgoertzelpi = 0;
|
||||||
|
let quantaA = 0;
|
||||||
|
let quantaINV = 0;
|
||||||
|
|
||||||
|
for( let rid = 0|0; rid < 2|0; rid++ )
|
||||||
|
{
|
||||||
|
contents += "<TR>";
|
||||||
|
for( let h = -2|min_harmonics; h <= max_harmonics; h++ )
|
||||||
|
{
|
||||||
|
let tquanta = (quanta&0xffffc);
|
||||||
|
let base = freq * h;
|
||||||
|
let next = freq * (h+1);
|
||||||
|
|
||||||
|
let tgoertzelpoint = tquanta;
|
||||||
|
tgoertzelpoint = ( target - base ) / ( next - base );
|
||||||
|
tgoertzelpoint = ((tgoertzelpoint%1)+1)%1;
|
||||||
|
|
||||||
|
quantaA = tquanta;
|
||||||
|
tgoertzelp = h;
|
||||||
|
if( h == -2 )
|
||||||
|
{
|
||||||
|
if( rid == 0 )
|
||||||
|
{
|
||||||
|
contents += "<TD COLSPAN=1 ROWSPAN=2>"
|
||||||
|
contents += "<SPAN ONCLICK='Goertz(" + n + ", " + freq * (h+tgoertzelpoint) + ", " + (tgoertzelpoint) + ", " + quantaA + ", 1)'>↑" + n + "</SPAN>";
|
||||||
|
contents += "</TD>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if( rid == 0 )
|
||||||
|
{
|
||||||
|
contents += DrawSpan( 1, 1, freq * (h+tgoertzelpoint), target, true );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
contents += DrawSpan( 1, 1, freq * (h-tgoertzelpoint), target, true );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contents += "</TR>";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
contents += "</TABLE>";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
document.getElementById( "TABLE" ).innerHTML = contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onLoad()
|
||||||
|
{
|
||||||
|
onLoadWebHidControl();
|
||||||
|
}
|
||||||
|
</SCRIPT>
|
||||||
|
</HEAD>
|
||||||
|
<BODY onLoad="onLoad()">
|
||||||
|
|
||||||
|
<TABLE WIDTH=100%>
|
||||||
|
<TR>
|
||||||
|
<TD COLSPAN=3>
|
||||||
|
<p>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.</p>
|
||||||
|
</TD>
|
||||||
|
<TD ROWSPAN=2 VALIGN=TOP WIDTH=100% ID=LiveGraphContainer>
|
||||||
|
<CANVAS WIDTH=100% HEIGHT=200 ID=LiveGraph> </CANVAS>
|
||||||
|
</TD>
|
||||||
|
</TR>
|
||||||
|
<TR>
|
||||||
|
<TD VALIGN=TOP>
|
||||||
|
<TABLE WIDTH=480>
|
||||||
|
<TR><TD>Crystal MHz</TD><TD><INPUT ID=crystalmhz VALUE=144></TD></TR>
|
||||||
|
<TR><TD>Target MHz</TD><TD><INPUT ID=targetmhz VALUE=27.019360></TD></TR>
|
||||||
|
<TR><TD>Quanta</TD><TD><INPUT ID=quanta VALUE=1024> (Goertzel's Only)</TD></TR>
|
||||||
|
<TR><TD>Quanta Search Range</TD><TD><INPUT ID=quantasearch VALUE=64> (Goertzel's Only)</TD></TR>
|
||||||
|
<TR><TD>Table Type</TD><TD>
|
||||||
|
<INPUT TYPE=RADIO ID=QUADRATURE NAME=computetype>Quadrature</INPUT>
|
||||||
|
<INPUT TYPE=RADIO ID=GOERTZELS NAME=computetype>Goertzels</INPUT>
|
||||||
|
<INPUT TYPE=RADIO ID=GOERTZEL2 NAME=computetype checked>Goertzel (unalign)</INPUT>
|
||||||
|
</TD></TR>
|
||||||
|
<TR><TD COLSPAN=2><INPUT TYPE=SUBMIT VALUE="Compute" ONCLICK="computeTable()"></TD></TR>
|
||||||
|
</TABLE>
|
||||||
|
</TD>
|
||||||
|
<TD VALIGN=TOP>
|
||||||
|
|
||||||
|
Live Control:
|
||||||
|
<TABLE><TR>
|
||||||
|
<TD><INPUT TYPE=SUBMIT onClick="reqConnect()" VALUE="Open Device" ID=connectButton>
|
||||||
|
<INPUT TYPE=SUBMIT onClick="toggleAudio()" VALUE="Play Audio" ID="ToggleAudioButton">
|
||||||
|
<INPUT TYPE=SUBMIT onClick="toggleAudioModulation()" VALUE="modulation" ID="ToggleAudioModulationButton">
|
||||||
|
<DIV ID=STATUS></DIV></TD>
|
||||||
|
</TR></TABLE>
|
||||||
|
<DIV ID="StatusPerf"></DIV>
|
||||||
|
</TD>
|
||||||
|
|
||||||
|
</TD>
|
||||||
|
</TR>
|
||||||
|
</TABLE>
|
||||||
|
|
||||||
|
<DIV ID=TABLE></DIV>
|
||||||
|
|
||||||
|
</BODY>
|
||||||
|
</HTML>
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,498 @@
|
|||||||
|
const expectedProductName = "CNLohr lolra ch32v203 goertzel test";
|
||||||
|
const filter = { vendorId : 0x1209, productId : 0xd035 };
|
||||||
|
let dev = null;
|
||||||
|
let loopAbort = false;
|
||||||
|
|
||||||
|
const IQHistoryLen = 4096;
|
||||||
|
var IQHistoryArray = new Uint32Array(IQHistoryLen);
|
||||||
|
var MPHistoryArray = new Float32Array(IQHistoryLen*2);
|
||||||
|
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;
|
||||||
|
console.log( msg );
|
||||||
|
}
|
||||||
|
|
||||||
|
function setStatusError( msg )
|
||||||
|
{
|
||||||
|
setStatus( "<FONT COLOR=RED>" + msg + "</FONT>" );
|
||||||
|
console.log( msg );
|
||||||
|
console.trace();
|
||||||
|
}
|
||||||
|
|
||||||
|
function tryConnect()
|
||||||
|
{
|
||||||
|
if( !navigator.hid )
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !dev )
|
||||||
|
{
|
||||||
|
navigator.hid.getDevices().then( (devices) =>
|
||||||
|
{
|
||||||
|
if( devices.length == 0 )
|
||||||
|
setStatusError( "No devices found. Open a device." );
|
||||||
|
else
|
||||||
|
devices.forEach( tryOpen );
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function closeDeviceTool()
|
||||||
|
{
|
||||||
|
loopAbort = false;
|
||||||
|
setStatusError( "Disconnected" );
|
||||||
|
}
|
||||||
|
|
||||||
|
var playingAudioProcessor = null;
|
||||||
|
var audioContext = null;
|
||||||
|
|
||||||
|
var targetModulation = 0;
|
||||||
|
var targetGain = 0.0;
|
||||||
|
|
||||||
|
function UpdateButtonNames()
|
||||||
|
{
|
||||||
|
document.getElementById( "ToggleAudioButton" ).value = ( targetGain > 0.0 ) ? "Stop Audio" : "Start Audio";
|
||||||
|
document.getElementById( "ToggleAudioModulationButton" ).value = ( targetModulation > 0.5 ) ? "FM" : "AM";
|
||||||
|
}
|
||||||
|
|
||||||
|
async function toggleAudioModulation()
|
||||||
|
{
|
||||||
|
if( playingAudioProcessor != null )
|
||||||
|
{
|
||||||
|
var newVal = 1 - targetModulation;
|
||||||
|
targetModulation = newVal;
|
||||||
|
}
|
||||||
|
UpdateButtonNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function toggleAudio()
|
||||||
|
{
|
||||||
|
if( playingAudioProcessor == null )
|
||||||
|
{
|
||||||
|
var bypass = '\
|
||||||
|
class PlayingAudioProcessor extends AudioWorkletProcessor {\
|
||||||
|
static get parameterDescriptors() {\
|
||||||
|
return [\
|
||||||
|
{ name: "gain", defaultValue: 0, },\
|
||||||
|
{ name: "sampleAdvance", defaultValue: 0.5, },\
|
||||||
|
]\
|
||||||
|
};\
|
||||||
|
constructor() {\
|
||||||
|
super();\
|
||||||
|
this.rbuffer = new Float32Array(8192); \
|
||||||
|
this.rbufferhead = 0|0; \
|
||||||
|
this.rbuffertail = 0|0; \
|
||||||
|
this.sampleplace = 0.0; \
|
||||||
|
this.dcoffset = 0.0; \
|
||||||
|
this.totalsampcount = 0|0; \
|
||||||
|
\
|
||||||
|
this.port.onmessage = (e) => { \
|
||||||
|
for( var i = 0|0; i < e.data.length|0; i++ ) \
|
||||||
|
{ \
|
||||||
|
let n = (this.rbufferhead + (1|0))%(8192|0); \
|
||||||
|
if( n == this.rbuffertail ) \
|
||||||
|
{ \
|
||||||
|
this.rbuffertail = (this.rbuffertail + (1|0))%(8192|0); \
|
||||||
|
console.log( `Overflow` ); \
|
||||||
|
} \
|
||||||
|
var vv = e.data[i]; \
|
||||||
|
this.dcoffset = this.dcoffset * 0.995 + vv * 0.005; \
|
||||||
|
this.rbuffer[this.rbufferhead] = vv - this.dcoffset; \
|
||||||
|
this.rbufferhead = n; \
|
||||||
|
} \
|
||||||
|
}; \
|
||||||
|
}\
|
||||||
|
\
|
||||||
|
process(inputs, outputs, parameters) {\
|
||||||
|
/*console.log( parameters.gain[0] );*/ \
|
||||||
|
/*console.log( this.ingestData );*/ \
|
||||||
|
let len = outputs[0][0].length; \
|
||||||
|
const sa = Math.fround( parameters.sampleAdvance[0] ); /*float*/ \
|
||||||
|
var s = Math.fround( this.sampleplace ); /*float*/ \
|
||||||
|
var tail = this.rbuffertail | 0; /* int*/ \
|
||||||
|
var tailnext = this.rbuffertail | 0; /* int*/ \
|
||||||
|
if( tail == this.rbufferhead ) { console.log( "Underflow " ); return true; }\
|
||||||
|
var tsamp = Math.fround( this.rbuffer[tail] ); \
|
||||||
|
var nsamp = Math.fround( this.rbuffer[tailnext] ); \
|
||||||
|
this.totalsampcount += len|0; \
|
||||||
|
for (let b = 0|0; b < len|0; b++) { \
|
||||||
|
s += sa; \
|
||||||
|
var excess = Math.floor( s ) | 0; \
|
||||||
|
if( excess > 0 ) \
|
||||||
|
{ \
|
||||||
|
s -= excess; \
|
||||||
|
tail = ( tail + (excess|0) ) % (8192|0); \
|
||||||
|
tailnext = ( tail + (1|0) ) % (8192|0); \
|
||||||
|
if( tail == this.rbufferhead ) { console.log( "Underflow" ); break; } \
|
||||||
|
tsamp = Math.fround( this.rbuffer[tail] ); \
|
||||||
|
nsamp = Math.fround( this.rbuffer[tailnext] ); \
|
||||||
|
} \
|
||||||
|
var valv = tsamp * (1.0-s) + nsamp * s; \
|
||||||
|
outputs[0][0][b] = valv*parameters.gain[0]; \
|
||||||
|
} \
|
||||||
|
/*console.log( tail + " " + this.rbuffertail + " " + tsamp + " " + nsamp );*/ \
|
||||||
|
this.rbuffertail = tail; \
|
||||||
|
this.sampleplace = s; \
|
||||||
|
return true; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
registerProcessor("playing-audio-processor", PlayingAudioProcessor);';
|
||||||
|
|
||||||
|
// The following mechanism does not work on Chrome.
|
||||||
|
// const dataURI = URL.createObjectURL( new Blob([bypass], { type: 'text/javascript', } ) );
|
||||||
|
|
||||||
|
|
||||||
|
// Extremely tricky trick to side-step local file:// CORS issues.
|
||||||
|
// https://stackoverflow.com/a/67125196/2926815
|
||||||
|
// https://stackoverflow.com/a/72180421/2926815
|
||||||
|
let blob = new Blob([bypass], {type: 'application/javascript'});
|
||||||
|
let reader = new FileReader();
|
||||||
|
await reader.readAsDataURL(blob);
|
||||||
|
let dataURI = await new Promise((res) => {
|
||||||
|
reader.onloadend = function () {
|
||||||
|
res(reader.result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
audioContext = new AudioContext();
|
||||||
|
|
||||||
|
await audioContext.audioWorklet.addModule(dataURI);
|
||||||
|
|
||||||
|
playingAudioProcessor = new AudioWorkletNode(
|
||||||
|
audioContext,
|
||||||
|
"playing-audio-processor"
|
||||||
|
);
|
||||||
|
playingAudioProcessor.connect(audioContext.destination);
|
||||||
|
audioContext.resume();
|
||||||
|
|
||||||
|
let gainParam = playingAudioProcessor.parameters.get( "gain" );
|
||||||
|
gainParam.setValueAtTime( 0, audioContext.currentTime );
|
||||||
|
}
|
||||||
|
|
||||||
|
var newVal = 0.1 - targetGain;
|
||||||
|
console.log( "Setting gain to: " + newVal );
|
||||||
|
let gainParam = playingAudioProcessor.parameters.get("gain");
|
||||||
|
gainParam.setValueAtTime( newVal, audioContext.currentTime);
|
||||||
|
targetGain = newVal;
|
||||||
|
document.getElementById( "ToggleAudioButton" ).value = ( newVal > 0.5 ) ? "Stop Audio" : "Start Audio";
|
||||||
|
UpdateButtonNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onLoadWebHidControl()
|
||||||
|
{
|
||||||
|
liveGraph = document.getElementById( "LiveGraph" );
|
||||||
|
liveGraph.addEventListener( "mousedown", graphClick );
|
||||||
|
liveGraph.addEventListener( "mouseup", graphClick );
|
||||||
|
|
||||||
|
UpdateButtonNames();
|
||||||
|
setTimeout( sendLoop, 1 );
|
||||||
|
|
||||||
|
if( !navigator.hid )
|
||||||
|
{
|
||||||
|
setStatusError( "Browser does not support HID." );
|
||||||
|
document.getElementById( "connectButton" ).hidden = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
navigator.hid.addEventListener("disconnect", (event) => { if( event.device.productName == expectedProductName ) closeDeviceTool(); } );
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout( () => { elapsedOK = true; }, 3000 );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function reqConnect()
|
||||||
|
{
|
||||||
|
loopAbort = true;
|
||||||
|
const initialization = navigator.hid.requestDevice( { filters: [ filter ] } );
|
||||||
|
initialization.then( gotUSBDevice );
|
||||||
|
initialization.catch( setStatusError );
|
||||||
|
}
|
||||||
|
|
||||||
|
function gotUSBDevice(result)
|
||||||
|
{
|
||||||
|
if( result.length < 1 )
|
||||||
|
{
|
||||||
|
setStatusError( "Error: No devices found" );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( result[0].productName != expectedProductName )
|
||||||
|
{
|
||||||
|
setStatusError( "Error: Wrong device name. Got " + result[0].productName + " Expected " + expectedProductName );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const thisDev = result[0];
|
||||||
|
|
||||||
|
tryOpen( thisDev );
|
||||||
|
}
|
||||||
|
|
||||||
|
function tryOpen( thisDev )
|
||||||
|
{
|
||||||
|
thisDev.open().then( ( result ) => {
|
||||||
|
if( result === undefined )
|
||||||
|
{
|
||||||
|
if( dev ) dev.close();
|
||||||
|
loopAbort = false;
|
||||||
|
dev = thisDev;
|
||||||
|
setStatus( "Connected." );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setStatusError( "Error: Could not open; " + result );
|
||||||
|
}
|
||||||
|
} ).catch( (e) => setStatusError( "Error: Could not open; " + e ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
let sendReport = null;
|
||||||
|
let receiveReport = null;
|
||||||
|
|
||||||
|
async function sendLoopError( e )
|
||||||
|
{
|
||||||
|
sendReport = null;
|
||||||
|
receiveReport = null;
|
||||||
|
if( dev ) await dev.close();
|
||||||
|
dev = null;
|
||||||
|
setStatusError( e );
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateWebHidDeviceWithParameters( paramlist )
|
||||||
|
{
|
||||||
|
var i = 0|0;
|
||||||
|
var arraySend = new Uint8Array(63);
|
||||||
|
for( var i = 0|0; i < paramlist.length|0; i++ )
|
||||||
|
{
|
||||||
|
var vv = paramlist[i] | 0;
|
||||||
|
arraySend[i*4+7] = (vv>>0)&0xff;
|
||||||
|
arraySend[i*4+8] = (vv>>8)&0xff;
|
||||||
|
arraySend[i*4+9] = (vv>>16)&0xff;
|
||||||
|
arraySend[i*4+10] = (vv>>24)&0xff;
|
||||||
|
}
|
||||||
|
arraySend[3] = paramlist.length | 0;
|
||||||
|
sendReport = dev.sendFeatureReport( 0xAC, arraySend ).catch( sendLoopError );
|
||||||
|
if( !sendReport ) sendLoopError( "error creating sendFeatureReport" );
|
||||||
|
}
|
||||||
|
|
||||||
|
FMiirphase = 0.0; /* for FM */
|
||||||
|
FMlastphase = 0.0; /* for FM */
|
||||||
|
FMphaseout = 0.0; /* for FM */
|
||||||
|
|
||||||
|
|
||||||
|
async function sendLoop()
|
||||||
|
{
|
||||||
|
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
||||||
|
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 )
|
||||||
|
{
|
||||||
|
receiveReport = dev.receiveFeatureReport( 0xAD ).catch( sendLoopError );
|
||||||
|
if( !receiveReport ) sendLoopError( "error creating receiveReport" );
|
||||||
|
|
||||||
|
frameNo++
|
||||||
|
|
||||||
|
const updateStatsPerfPer = 4;
|
||||||
|
if( frameNo % updateStatsPerfPer == 0 )
|
||||||
|
{
|
||||||
|
let thisTime = performance.now();
|
||||||
|
let deltaTime = thisTime - lastTime;
|
||||||
|
let kBsec = (255*1000/1024*updateStatsPerfPer)/(deltaTime);
|
||||||
|
let xActionSec = (2*updateStatsPerfPer*1000)/(deltaTime);
|
||||||
|
kBsecAvg = kBsecAvg * 0.9 + kBsec * 0.1;
|
||||||
|
xActionSecAvg = xActionSecAvg * 0.9 + xActionSec * 0.1;
|
||||||
|
|
||||||
|
document.getElementById( "StatusPerf" ).innerHTML =
|
||||||
|
(kBsecAvg).toFixed(2) + " kBytes/s<br>" +
|
||||||
|
(xActionSecAvg).toFixed(2) + "transactions/sec<br>" +
|
||||||
|
"Count: " + goodCount + " / " + 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;
|
||||||
|
for( var i = (IQHistoryHead-1) & (IQHistoryLen-1); i != IQHistoryHead|0; i = (i - 1 + IQHistoryLen) & (IQHistoryLen-1) )
|
||||||
|
{
|
||||||
|
let power = MPHistoryArray[i*2+0]; //Math.sqrt( real * real + imag * imag ) * mulcoeff;
|
||||||
|
let phase = MPHistoryArray[i*2+1]; //Math.atan2( real, imag ) * 0.159155078*0.5;
|
||||||
|
ctx.fillRect(x,power*120+10,2,2);
|
||||||
|
ctx.fillRect(x,phase*80+110,2,2);
|
||||||
|
|
||||||
|
let v = IQHistoryArray[i];
|
||||||
|
let real = (v >> 16);
|
||||||
|
let imag = (v & 0xffff);
|
||||||
|
if( real > 32767 ) real -= 65536;
|
||||||
|
if( imag > 32767 ) imag -= 65536;
|
||||||
|
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;
|
||||||
|
x++;
|
||||||
|
if( lot > 0 )
|
||||||
|
{
|
||||||
|
ctx.globalAlpha = lot;
|
||||||
|
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 )
|
||||||
|
{
|
||||||
|
await sendReport;
|
||||||
|
}
|
||||||
|
if( receiveReport )
|
||||||
|
{
|
||||||
|
let receiveData = await receiveReport;
|
||||||
|
if( receiveData && receiveData.buffer )
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
let sample_divisor = data[2]&0xffff;
|
||||||
|
let demodbuffer = new Float32Array(numq);
|
||||||
|
let mulcoeff = 100.0 / lastIntensity;
|
||||||
|
for( var i = 0|0; i < numq; i++ )
|
||||||
|
{
|
||||||
|
let vv = IQHistoryArray[IQHistoryHead] = data[i+3];
|
||||||
|
let vi = vv >> 16;
|
||||||
|
let vq = vv & 0xffff;
|
||||||
|
if( vi >= 32768 ) vi = vi-65535;
|
||||||
|
if( vq >= 32768 ) vq = vq-65535;
|
||||||
|
let power = Math.sqrt( vi * vi + vq * vq ) * mulcoeff;
|
||||||
|
let phase = Math.atan2( vi, vq ) * 0.159155078 + 0.5;
|
||||||
|
MPHistoryArray[IQHistoryHead*2+0] = power; //Math.sqrt( real * real + imag * imag ) * mulcoeff;
|
||||||
|
MPHistoryArray[IQHistoryHead*2+1] = phase; //Math.atan2( real, imag ) * 0.159155078*0.5;
|
||||||
|
if( targetModulation == 0 )
|
||||||
|
{ /* AM */
|
||||||
|
demodbuffer[i] = power;
|
||||||
|
}
|
||||||
|
else if( targetModulation == 1 )
|
||||||
|
{ /* FM */
|
||||||
|
var diffphase = phase - FMlastphase;
|
||||||
|
FMlastphase = phase;
|
||||||
|
if( diffphase - FMiirphase < 0.0 ) diffphase += 1.0;
|
||||||
|
if( diffphase - FMiirphase > 1.0 ) diffphase -= 1.0;
|
||||||
|
FMiirphase = FMiirphase * 0.999 + diffphase * 0.001;
|
||||||
|
diffphase -= FMiirphase;
|
||||||
|
if( diffphase > 0.5 ) diffphase -= 1;
|
||||||
|
if( diffphase <-0.5 ) diffphase += 1;
|
||||||
|
var po = FMphaseout = FMphaseout * 0.993 + diffphase;
|
||||||
|
if( po < 0.0 ) po += 1.0;
|
||||||
|
if( po > 1.0 ) po -= 1.0;
|
||||||
|
demodbuffer[i] = po;
|
||||||
|
}
|
||||||
|
|
||||||
|
IQHistoryHead = (IQHistoryHead+1)%IQHistoryLen;
|
||||||
|
}
|
||||||
|
lastIntensity = intensity;
|
||||||
|
lastNumQ = numq;
|
||||||
|
lastTotalTime = time_total;
|
||||||
|
lastTimeUsed = time_used;
|
||||||
|
|
||||||
|
if( audioContext != null && playingAudioProcessor != null )
|
||||||
|
{
|
||||||
|
// TODO: Use crystalmhz
|
||||||
|
let sampleAdvance = (144000000.0/sample_divisor) / audioContext.sampleRate;
|
||||||
|
let sampleAdvanceParam = playingAudioProcessor.parameters.get("sampleAdvance");
|
||||||
|
sampleAdvanceParam.setValueAtTime( sampleAdvance, audioContext.currentTime);
|
||||||
|
playingAudioProcessor.port.postMessage( demodbuffer );
|
||||||
|
}
|
||||||
|
goodCount++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
badCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( loopAbort )
|
||||||
|
{
|
||||||
|
if( dev )
|
||||||
|
{
|
||||||
|
console.log( "Loop Aborting, Dev Closing" );
|
||||||
|
await dev.close();
|
||||||
|
console.log( "Loop Aborting, Dev Closed." );
|
||||||
|
dev = null;
|
||||||
|
}
|
||||||
|
await sleep(100);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Try opening dev.
|
||||||
|
console.log( "Attempting reconnect." );
|
||||||
|
tryConnect();
|
||||||
|
goodCount = 0;
|
||||||
|
badCount = 0;
|
||||||
|
let i = 0|0;
|
||||||
|
for( i = 0; i < 10; i++ )
|
||||||
|
{
|
||||||
|
await sleep(100);
|
||||||
|
if( dev )
|
||||||
|
{
|
||||||
|
//break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user