From 0a6e76034f60bf4654d3045cebeb658edd698d00 Mon Sep 17 00:00:00 2001 From: cnlohr Date: Sun, 7 Jul 2024 03:44:51 -0700 Subject: [PATCH] Progress on goertzel's work in conjunction with web. --- ch32v/ch32v003fun | 2 +- ch32v/ch32v203-fft/README.md | 4 + ch32v/ch32v203-fft/adcfft.c | 4 +- ch32v/ch32v203-goertzel/Makefile | 2 +- ch32v/ch32v203-goertzel/adcgoertzel.c | 156 ++++-- ch32v/ch32v203-goertzel/funconfig.h | 1 + ch32v/lib/calculator.html | 38 +- ch32v/lib/ssd1306.h | 713 -------------------------- ch32v/lib/ssd1306_i2c.h | 374 -------------- ch32v/lib/webhidcontrol.js | 226 ++++++++ 10 files changed, 372 insertions(+), 1148 deletions(-) create mode 100644 ch32v/ch32v203-fft/README.md delete mode 100644 ch32v/lib/ssd1306.h delete mode 100644 ch32v/lib/ssd1306_i2c.h create mode 100644 ch32v/lib/webhidcontrol.js diff --git a/ch32v/ch32v003fun b/ch32v/ch32v003fun index 3f6f5f7..aa91922 160000 --- a/ch32v/ch32v003fun +++ b/ch32v/ch32v003fun @@ -1 +1 @@ -Subproject commit 3f6f5f7db5ae8e2524cf74cd85764e894aaa0653 +Subproject commit aa91922a0690ab5c8f069a628d5a1c9aa3bcacbe diff --git a/ch32v/ch32v203-fft/README.md b/ch32v/ch32v203-fft/README.md new file mode 100644 index 0000000..23b672e --- /dev/null +++ b/ch32v/ch32v203-fft/README.md @@ -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 diff --git a/ch32v/ch32v203-fft/adcfft.c b/ch32v/ch32v203-fft/adcfft.c index 07a89c1..cb38d72 100644 --- a/ch32v/ch32v203-fft/adcfft.c +++ b/ch32v/ch32v203-fft/adcfft.c @@ -62,7 +62,7 @@ SOFTWARE. #include #define SH1107_128x128 - +#define SSD1306_REMAP_I2C #include "ssd1306_i2c.h" #include "ssd1306.h" @@ -86,7 +86,7 @@ void SetupADC() // XXX TODO -look into PGA // XXX TODO - Look into tag-teaming the ADCs - // PDA is analog input chl 7 + // PA7 is analog input chl 7 GPIOA->CFGLR &= ~(0xf<<(4*7)); // CNF = 00: Analog, MODE = 00: Input // ADC CLK is chained off of APB2. diff --git a/ch32v/ch32v203-goertzel/Makefile b/ch32v/ch32v203-goertzel/Makefile index 0ba7d27..f47f937 100644 --- a/ch32v/ch32v203-goertzel/Makefile +++ b/ch32v/ch32v203-goertzel/Makefile @@ -5,7 +5,7 @@ TARGET_MCU:=CH32V203G6U6 TARGET_MCU_PACKAGE:=CH32V203G6U6 CH32V003FUN:=../ch32v003fun/ch32v003fun -EXTRA_CFLAGS:=-Wno-unused-function -I../../lib -I../lib +EXTRA_CFLAGS:=-Wno-unused-function -I../../lib -I../lib -I../ch32v003fun/examples_v20x/otg_device -I. include ../ch32v003fun/ch32v003fun/ch32v003fun.mk diff --git a/ch32v/ch32v203-goertzel/adcgoertzel.c b/ch32v/ch32v203-goertzel/adcgoertzel.c index 183f62c..1257dc8 100644 --- a/ch32v/ch32v203-goertzel/adcgoertzel.c +++ b/ch32v/ch32v203-goertzel/adcgoertzel.c @@ -63,73 +63,72 @@ SOFTWARE. #include #define SH1107_128x128 - -//#define PWM_OUTPUT +#define SSD1306_REMAP_I2C +#define PWM_OUTPUT #define ENABLE_OLED #include "ssd1306_i2c.h" #include "ssd1306.h" +#include "./usb_config.h" +#include "../ch32v003fun/examples_v20x/otg_device/otgusb.h" #define ADC_BUFFSIZE 1024 volatile uint16_t adc_buffer[ADC_BUFFSIZE]; -//#define PWM_PERIOD (31-1) -//const int32_t g_goertzel_omega_per_sample = 1238618695; // 47/256 -> 27.01920 MHz -//const int32_t g_goertzel_coefficient = 870249096; -//const int32_t g_goertzel_coefficient_s = 1963250500; +int g_volume_pwm = 127; // 0 - 127 (100%) (but you can go over 100) #if 0 -#define PWM_PERIOD (30-1) -#define GOERTZEL_BUFFER (752) -const int32_t g_goertzel_omega_per_sample = 2485087396; // 0.368351 of whole per step / 27.031915MHz -const int32_t g_goertzel_coefficient = -1453756170; -const int32_t g_goertzel_coefficient_s = 1580594514; +int g_pwm_period = (30-1); +int g_goertzel_buffer = (752); +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); +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 -#define PWM_PERIOD (30-1) -#define GOERTZEL_BUFFER (180) -const int32_t g_goertzel_omega_per_sample = 5509657063; // 0.816667 of whole per step / 0.880000MHz -const int32_t g_goertzel_coefficient = 873460290; -const int32_t g_goertzel_coefficient_s = -1961823932; -#endif - -#if 0 -#define PWM_PERIOD (31-1) -#define GOERTZEL_BUFFER (412) +int g_pwm_period = (31-1); +int g_goertzel_buffer = (412); 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 -#define PWM_PERIOD (30-1) -#define GOERTZEL_BUFFER (576) +int g_pwm_period = (30-1); +int g_goertzel_buffer = (576); const int32_t g_goertzel_omega_per_sample = 1264972285; // 0.187500 of whole per step / 90.300000MHz const int32_t g_goertzel_coefficient = 821806413; const int32_t g_goertzel_coefficient_s = 1984016189; #endif #if 0 -#define PWM_PERIOD (30-1) -#define GOERTZEL_BUFFER (320) +int g_pwm_period = (30-1); +int g_goertzel_buffer = (320); 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 -#define PWM_PERIOD (30-1) -#define GOERTZEL_BUFFER (384) +int g_pwm_period = (30-1); +int g_goertzel_buffer = (384); 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 1 -#define PWM_PERIOD (30-1) -#define GOERTZEL_BUFFER (336) +#if 0 +int g_pwm_period = (30-1); +int g_goertzel_buffer = (336); 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; @@ -228,16 +227,15 @@ static void SetupTimer1() RCC->APB2PRSTR &= ~RCC_APB2Periph_TIM1; TIM1->PSC = 0; // Prescalar to 0x0000 (so, 48MHz base clock) - TIM1->ATRLR = PWM_PERIOD; + TIM1->ATRLR = g_pwm_period; #ifdef PWM_OUTPUT // PA9 = T1CH2. - GPIOA->CFGHR &= ~(0xf<<(4*1)); - GPIOA->CFGHR |= (GPIO_Speed_2MHz | GPIO_CNF_OUT_PP_AF)<<(4*1); + 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; // Actual duty cycle (Off to begin with) + TIM1->CH2CVR = 5; // Set duty cycle somewhere random. // Enable TIM1 outputs TIM1->BDTR |= 0xc000;//TIM_MOE; @@ -303,7 +301,6 @@ void DMA1_Channel1_IRQHandler( void ) // Add a tiny bias to the ADC to help keep goertz in range. const int adc_offset = (-2048) << INFADC; - while( adc_tail != adc_buffer_end ) { @@ -356,7 +353,7 @@ void DMA1_Channel1_IRQHandler( void ) adc_tail+=4; goertzel_samples+=4; if( adc_tail == adc_buffer_top ) adc_tail = adc_buffer; - if( goertzel_samples == GOERTZEL_BUFFER ) + if( goertzel_samples == g_goertzel_buffer ) { g_goertzelp_store = goertzel - (g_goertzel_omega_per_sample>>(29-16)); g_goertzelp2_store = goertzelp; @@ -382,12 +379,9 @@ void DMA1_Channel1_IRQHandler( void ) #ifdef PWM_OUTPUT - - intensity = intensity * PWM_PERIOD / (intensity_max>>7); - - if( intensity >= PWM_PERIOD-1 ) intensity = PWM_PERIOD-2; + intensity = intensity * g_volume_pwm * g_pwm_period / (intensity_max>>(7-7)); + 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 @@ -410,13 +404,17 @@ void DMA1_Channel1_IRQHandler( void ) //GPIOD->BSHR = 1<<16; // Turn off GPIOD0 for profiling } +static inline uint32_t gets2() +{ + uint32_t ret; + asm volatile( "mv %[ret], s2" : [ret]"=&r"(ret) ); + return ret; +} void InnerLoop() { while(1){ - - int k; #if 0 int adcz = adc_buffer[0]; for( k = 0; k < 128; k++ ) @@ -470,13 +468,17 @@ void InnerLoop() } #ifdef ENABLE_OLED - ssd1306_refresh(); - ssd1306_setbuf(0); - char cts[32]; snprintf( cts, 32, "%d %d", intensity_max, intensity ); 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 @@ -484,6 +486,8 @@ void InnerLoop() // 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 "); + } } @@ -530,7 +534,7 @@ int main() #ifdef ENABLE_OLED ssd1306_i2c_setup(); - uint8_t ret = ssd1306_i2c_init(); + ssd1306_i2c_init(); ssd1306_init(); ssd1306_setbuf(0); #endif @@ -565,5 +569,63 @@ int main() SetupTimer1(); + + funPinMode( PA0, GPIO_CFGLR_OUT_10Mhz_PP ); + + USBOTGSetup(); InnerLoop(); } + + + + + + + + + + +uint8_t scratchpad[256]; + + +int HandleHidUserSetReportSetup( struct _USBState * ctx, tusb_control_request_t * req ) +{ + int id = req->wValue & 0xff; + if( id == 0xaa ) + { + // memset( scratchpad, 0x55, sizeof(scratchpad) ); + //printf( "SET REPORT! %d [%02x]\n", req->wLength, scratchpad[200] ); + 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 ) + { + //printf( "GET REPORT! %d\n", req->wLength ); + ctx->pCtrlPayloadPtr = scratchpad; + return 255; + } + 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 ) +{ + return; +} + diff --git a/ch32v/ch32v203-goertzel/funconfig.h b/ch32v/ch32v203-goertzel/funconfig.h index eb91275..8edd6b5 100644 --- a/ch32v/ch32v203-goertzel/funconfig.h +++ b/ch32v/ch32v203-goertzel/funconfig.h @@ -5,6 +5,7 @@ #define FUNCONF_USE_UARTPRINTF 0 #define FUNCONF_USE_HSE 1 #define FUNCONF_SYSTICK_USE_HCLK 1 +#define FUNCONF_ENABLE_HPE 0 #endif diff --git a/ch32v/lib/calculator.html b/ch32v/lib/calculator.html index 327270a..7d77641 100644 --- a/ch32v/lib/calculator.html +++ b/ch32v/lib/calculator.html @@ -1,14 +1,20 @@ + + + + + - +

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.

+ + + +
+ @@ -198,7 +210,13 @@ function computeTable()
Crystal MHz
Target MHz
Quanta (Goertzel's Algorithm Only)
Table TypeQuadratureGoertzels
- +
+Live Control: +
+
+
diff --git a/ch32v/lib/ssd1306.h b/ch32v/lib/ssd1306.h deleted file mode 100644 index 7aa2b03..0000000 --- a/ch32v/lib/ssd1306.h +++ /dev/null @@ -1,713 +0,0 @@ -/* - * Single-File-Header for using SPI OLED - * 05-05-2023 E. Brombaugh - */ - -#ifndef _SSD1306_H -#define _SSD1306_H - -#include -#include -#include "font_8x8.h" - -// comfortable packet size for this OLED -#define SSD1306_PSZ 32 - -// characteristics of each type -#if !defined (SSD1306_64X32) && !defined (SSD1306_128X32) && !defined (SSD1306_128X64) && !defined (SH1107_128x128) - #error "Please define the SSD1306_WXH resolution used in your application" -#endif - -#ifdef SSD1306_64X32 -#define SSD1306_W 64 -#define SSD1306_H 32 -#define SSD1306_FULLUSE -#define SSD1306_OFFSET 32 -#endif - -#ifdef SSD1306_128X32 -#define SSD1306_W 128 -#define SSD1306_H 32 -#define SSD1306_OFFSET 0 -#endif - -#ifdef SSD1306_128X64 -#define SSD1306_W 128 -#define SSD1306_H 64 -#define SSD1306_FULLUSE -#define SSD1306_OFFSET 0 -#endif - -#ifdef SH1107_128x128 -#define SH1107 -#define SSD1306_FULLUSE -#define SSD1306_W 128 -#define SSD1306_H 128 -#define SSD1306_FULLUSE -#define SSD1306_OFFSET 0 -#endif - -/* - * send OLED command byte - */ -uint8_t ssd1306_cmd(uint8_t cmd) -{ - ssd1306_pkt_send(&cmd, 1, 1); - return 0; -} - -/* - * send OLED data packet (up to 32 bytes) - */ -uint8_t ssd1306_data(uint8_t *data, uint8_t sz) -{ - ssd1306_pkt_send(data, sz, 0); - return 0; -} - -#define SSD1306_SETCONTRAST 0x81 -#define SSD1306_SEGREMAP 0xA0 -#define SSD1306_DISPLAYALLON_RESUME 0xA4 -#define SSD1306_DISPLAYALLON 0xA5 -#define SSD1306_NORMALDISPLAY 0xA6 -#define SSD1306_INVERTDISPLAY 0xA7 -#define SSD1306_DISPLAYOFF 0xAE -#define SSD1306_DISPLAYON 0xAF -#define SSD1306_SETDISPLAYOFFSET 0xD3 -#define SSD1306_SETCOMPINS 0xDA -#define SSD1306_SETVCOMDETECT 0xDB -#define SSD1306_SETDISPLAYCLOCKDIV 0xD5 -#define SSD1306_SETPRECHARGE 0xD9 -#define SSD1306_SETMULTIPLEX 0xA8 -#define SSD1306_SETLOWCOLUMN 0x00 -#define SSD1306_SETHIGHCOLUMN 0x10 -#define SSD1306_SETSTARTLINE 0x40 -#define SSD1306_MEMORYMODE 0x20 -#define SSD1306_COLUMNADDR 0x21 -#define SSD1306_PAGEADDR 0x22 -#define SSD1306_COMSCANINC 0xC0 -#define SSD1306_COMSCANDEC 0xC8 -#define SSD1306_CHARGEPUMP 0x8D -#define SSD1306_EXTERNALVCC 0x1 -#define SSD1306_SWITCHCAPVCC 0x2 -#define SSD1306_TERMINATE_CMDS 0xFF - -/* choose VCC mode */ -#define SSD1306_EXTERNALVCC 0x1 -#define SSD1306_SWITCHCAPVCC 0x2 -//#define vccstate SSD1306_EXTERNALVCC -#define vccstate SSD1306_SWITCHCAPVCC - -// OLED initialization commands for 128x32 -const uint8_t ssd1306_init_array[] = -{ -#ifdef SH1107 - SSD1306_DISPLAYOFF, // Turn OLED off - 0x00, // Low column - 0x10, // High column - 0xb0, // Page address - 0xdc, 0x00, // Set Display Start Line (Where in memory it reads from) - SSD1306_SETCONTRAST, 0x6f, // Set constrast - SSD1306_COLUMNADDR, // Set memory addressing mode - SSD1306_DISPLAYALLON_RESUME, // normal (as opposed to invert colors, always on or off.) - SSD1306_SETMULTIPLEX, (SSD1306_H-1), // Iterate over all 128 rows (Multiplex Ratio) - SSD1306_SETDISPLAYOFFSET, 0x00, // Set display offset // Where this appears on-screen (Some displays will be different) - SSD1306_SETDISPLAYCLOCKDIV, 0xf0, // Set precharge properties. THIS IS A LIE This has todo with timing. <<< This makes it go brrrrrrrrr - SSD1306_SETPRECHARGE, 0x1d, // Set pre-charge period (This controls brightness) - SSD1306_SETVCOMDETECT, 0x35, // Set vcomh - SSD1306_SETSTARTLINE | 0x0, // 0x40 | line - 0xad, 0x80, // Set Charge pump - SSD1306_SEGREMAP, 0x01, // Default mapping - SSD1306_SETPRECHARGE, 0x06, // ???? No idea what this does, but this looks best. - SSD1306_SETCONTRAST, 0xfe, // Set constrast - SSD1306_SETVCOMDETECT, 0xfe, // Set vcomh - SSD1306_SETMULTIPLEX, (SSD1306_H-1), // 128-wide. - SSD1306_DISPLAYON, // Display on. -#else - SSD1306_DISPLAYOFF, // 0xAE - SSD1306_SETDISPLAYCLOCKDIV, // 0xD5 - 0x80, // the suggested ratio 0x80 - SSD1306_SETMULTIPLEX, // 0xA8 -#ifdef SSD1306_64X32 - 0x1F, // for 64-wide displays -#else - 0x3F, // for 128-wide displays -#endif - SSD1306_SETDISPLAYOFFSET, // 0xD3 - 0x00, // no offset - SSD1306_SETSTARTLINE | 0x0, // 0x40 | line - SSD1306_CHARGEPUMP, // 0x8D - 0x14, // enable? - SSD1306_MEMORYMODE, // 0x20 - 0x00, // 0x0 act like ks0108 - SSD1306_SEGREMAP | 0x1, // 0xA0 | bit - SSD1306_COMSCANDEC, - SSD1306_SETCOMPINS, // 0xDA - 0x12, // - SSD1306_SETCONTRAST, // 0x81 - 0x8F, - SSD1306_SETPRECHARGE, // 0xd9 - 0xF1, - SSD1306_SETVCOMDETECT, // 0xDB - 0x40, - SSD1306_DISPLAYALLON_RESUME, // 0xA4 - SSD1306_NORMALDISPLAY, // 0xA6 - SSD1306_DISPLAYON, // 0xAF --turn on oled panel -#endif - SSD1306_TERMINATE_CMDS // 0xFF --fake command to mark end -}; - -// the display buffer -uint8_t ssd1306_buffer[SSD1306_W*SSD1306_H/8]; - -/* - * set the buffer to a color - */ -void ssd1306_setbuf(uint8_t color) -{ - memset(ssd1306_buffer, color ? 0xFF : 0x00, sizeof(ssd1306_buffer)); -} - -#ifndef SSD1306_FULLUSE -/* - * expansion array for OLED with every other row unused - */ -const uint8_t expand[16] = -{ - 0x00,0x02,0x08,0x0a, - 0x20,0x22,0x28,0x2a, - 0x80,0x82,0x88,0x8a, - 0xa0,0xa2,0xa8,0xaa, -}; -#endif - -/* - * Send the frame buffer - */ -void ssd1306_refresh(void) -{ - uint16_t i; - -#ifdef SH1107 - - ssd1306_cmd(SSD1306_MEMORYMODE); // vertical addressing mode. - - for(i=0;i>4) ); - ssd1306_data(&ssd1306_buffer[i*4*SSD1306_PSZ+0*SSD1306_PSZ], SSD1306_PSZ); - ssd1306_data(&ssd1306_buffer[i*4*SSD1306_PSZ+1*SSD1306_PSZ], SSD1306_PSZ); - ssd1306_data(&ssd1306_buffer[i*4*SSD1306_PSZ+2*SSD1306_PSZ], SSD1306_PSZ); - ssd1306_data(&ssd1306_buffer[i*4*SSD1306_PSZ+3*SSD1306_PSZ], SSD1306_PSZ); - } -#else - ssd1306_cmd(SSD1306_COLUMNADDR); - ssd1306_cmd(SSD1306_OFFSET); // Column start address (0 = reset) - ssd1306_cmd(SSD1306_OFFSET+SSD1306_W-1); // Column end address (127 = reset) - - ssd1306_cmd(SSD1306_PAGEADDR); - ssd1306_cmd(0); // Page start address (0 = reset) - ssd1306_cmd(7); // Page end address - -#ifdef SSD1306_FULLUSE - /* for fully used rows just plow thru everything */ - for(i=0;i>4)&0xf]; - - /* send PSZ block of data */ - ssd1306_data(tbuf, SSD1306_PSZ); - } - } -#endif -#endif - -} - -/* - * plot a pixel in the buffer - */ -void ssd1306_drawPixel(uint8_t x, uint8_t y, uint8_t color) -{ - uint16_t addr; - - /* clip */ - if(x >= SSD1306_W) - return; - if(y >= SSD1306_H) - return; - - /* compute buffer address */ - addr = x + SSD1306_W*(y/8); - - /* set/clear bit in buffer */ - if(color) - ssd1306_buffer[addr] |= (1<<(y&7)); - else - ssd1306_buffer[addr] &= ~(1<<(y&7)); -} - -/* - * plot a pixel in the buffer - */ -void ssd1306_xorPixel(uint8_t x, uint8_t y) -{ - uint16_t addr; - - /* clip */ - if(x >= SSD1306_W) - return; - if(y >= SSD1306_H) - return; - - /* compute buffer address */ - addr = x + SSD1306_W*(y/8); - - ssd1306_buffer[addr] ^= (1<<(y&7)); -} - -/* - * draw a an image from an array, directly into to the display buffer - * the color modes allow for overwriting and even layering (sprites!) - */ -void ssd1306_drawImage(uint8_t x, uint8_t y, const unsigned char* input, uint8_t width, uint8_t height, uint8_t color_mode) { - uint8_t x_absolute; - uint8_t y_absolute; - uint8_t pixel; - uint8_t bytes_to_draw = width / 8; - uint16_t buffer_addr; - - for (uint8_t line = 0; line < height; line++) { - y_absolute = y + line; - if (y_absolute >= SSD1306_H) { - break; - } - - // SSD1306 is in vertical mode, yet we want to draw horizontally, which necessitates assembling the output bytes from the input data - // bitmask for current pixel in vertical (output) byte - uint8_t v_mask = 1 << (y_absolute & 7); - - for (uint8_t byte = 0; byte < bytes_to_draw; byte++) { - uint8_t input_byte = input[byte + line * bytes_to_draw]; - - for (pixel = 0; pixel < 8; pixel++) { - x_absolute = x + 8 * (bytes_to_draw - byte) + pixel; - if (x_absolute >= SSD1306_W) { - break; - } - // looking at the horizontal display, we're drawing bytes bottom to top, not left to right, hence y / 8 - buffer_addr = x_absolute + SSD1306_W * (y_absolute / 8); - // state of current pixel - uint8_t input_pixel = input_byte & (1 << pixel); - - switch (color_mode) { - case 0: - // write pixels as they are - ssd1306_buffer[buffer_addr] = (ssd1306_buffer[buffer_addr] & ~v_mask) | (input_pixel ? v_mask : 0); - break; - case 1: - // write pixels after inversion - ssd1306_buffer[buffer_addr] = (ssd1306_buffer[buffer_addr] & ~v_mask) | (!input_pixel ? v_mask : 0); - break; - case 2: - // 0 clears pixel - ssd1306_buffer[buffer_addr] &= input_pixel ? 0xFF : ~v_mask; - break; - case 3: - // 1 sets pixel - ssd1306_buffer[buffer_addr] |= input_pixel ? v_mask : 0; - break; - case 4: - // 0 sets pixel - ssd1306_buffer[buffer_addr] |= !input_pixel ? v_mask : 0; - break; - case 5: - // 1 clears pixel - ssd1306_buffer[buffer_addr] &= input_pixel ? ~v_mask : 0xFF; - break; - } - } - #if SSD1306_LOG_IMAGE == 1 - printf("%02x ", input_byte); - #endif - } - #if SSD1306_LOG_IMAGE == 1 - printf("\n\r"); - #endif - } -} - -/* - * fast vert line - */ -void ssd1306_drawFastVLine(uint8_t x, uint8_t y, uint8_t h, uint8_t color) -{ - // clipping - if((x >= SSD1306_W) || (y >= SSD1306_H)) return; - if((y+h-1) >= SSD1306_H) h = SSD1306_H-y; - while(h--) - { - ssd1306_drawPixel(x, y++, color); - } -} - -/* - * fast horiz line - */ -void ssd1306_drawFastHLine(uint8_t x, uint8_t y, uint8_t w, uint8_t color) -{ - // clipping - if((x >= SSD1306_W) || (y >= SSD1306_H)) return; - if((x+w-1) >= SSD1306_W) w = SSD1306_W-x; - - while (w--) - { - ssd1306_drawPixel(x++, y, color); - } -} - -/* - * abs() helper function for line drawing - */ -int16_t gfx_abs(int16_t x) -{ - return (x<0) ? -x : x; -} - -/* - * swap() helper function for line drawing - */ -void gfx_swap(uint16_t *z0, uint16_t *z1) -{ - uint16_t temp = *z0; - *z0 = *z1; - *z1 = temp; -} - -/* - * Bresenham line draw routine swiped from Wikipedia - */ -void ssd1306_drawLine(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint8_t color) -{ - int16_t steep; - int16_t deltax, deltay, error, ystep, x, y; - - /* flip sense 45deg to keep error calc in range */ - steep = (gfx_abs(y1 - y0) > gfx_abs(x1 - x0)); - - if(steep) - { - gfx_swap(&x0, &y0); - gfx_swap(&x1, &y1); - } - - /* run low->high */ - if(x0 > x1) - { - gfx_swap(&x0, &x1); - gfx_swap(&y0, &y1); - } - - /* set up loop initial conditions */ - deltax = x1 - x0; - deltay = gfx_abs(y1 - y0); - error = deltax/2; - y = y0; - if(y0 < y1) - ystep = 1; - else - ystep = -1; - - /* loop x */ - for(x=x0;x<=x1;x++) - { - /* plot point */ - if(steep) - /* flip point & plot */ - ssd1306_drawPixel(y, x, color); - else - /* just plot */ - ssd1306_drawPixel(x, y, color); - - /* update error */ - error = error - deltay; - - /* update y */ - if(error < 0) - { - y = y + ystep; - error = error + deltax; - } - } -} - -/* - * draws a circle - */ -void ssd1306_drawCircle(int16_t x, int16_t y, int16_t radius, int8_t color) -{ - /* Bresenham algorithm */ - int16_t x_pos = -radius; - int16_t y_pos = 0; - int16_t err = 2 - 2 * radius; - int16_t e2; - - do { - ssd1306_drawPixel(x - x_pos, y + y_pos, color); - ssd1306_drawPixel(x + x_pos, y + y_pos, color); - ssd1306_drawPixel(x + x_pos, y - y_pos, color); - ssd1306_drawPixel(x - x_pos, y - y_pos, color); - e2 = err; - if (e2 <= y_pos) { - err += ++y_pos * 2 + 1; - if(-x_pos == y_pos && e2 <= x_pos) { - e2 = 0; - } - } - if (e2 > x_pos) { - err += ++x_pos * 2 + 1; - } - } while (x_pos <= 0); -} - -/* - * draws a filled circle - */ -void ssd1306_fillCircle(int16_t x, int16_t y, int16_t radius, int8_t color) -{ - /* Bresenham algorithm */ - int16_t x_pos = -radius; - int16_t y_pos = 0; - int16_t err = 2 - 2 * radius; - int16_t e2; - - do { - ssd1306_drawPixel(x - x_pos, y + y_pos, color); - ssd1306_drawPixel(x + x_pos, y + y_pos, color); - ssd1306_drawPixel(x + x_pos, y - y_pos, color); - ssd1306_drawPixel(x - x_pos, y - y_pos, color); - ssd1306_drawFastHLine(x + x_pos, y + y_pos, 2 * (-x_pos) + 1, color); - ssd1306_drawFastHLine(x + x_pos, y - y_pos, 2 * (-x_pos) + 1, color); - e2 = err; - if (e2 <= y_pos) { - err += ++y_pos * 2 + 1; - if(-x_pos == y_pos && e2 <= x_pos) { - e2 = 0; - } - } - if(e2 > x_pos) { - err += ++x_pos * 2 + 1; - } - } while(x_pos <= 0); -} - -/* - * draw a rectangle - */ -void ssd1306_drawRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color) -{ - ssd1306_drawFastVLine(x, y, h, color); - ssd1306_drawFastVLine(x+w-1, y, h, color); - ssd1306_drawFastHLine(x, y, w, color); - ssd1306_drawFastHLine(x, y+h-1, w, color); -} - -/* - * fill a rectangle - */ -void ssd1306_fillRect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, uint8_t color) -{ - uint8_t m, n=y, iw = w; - - /* scan vertical */ - while(h--) - { - m=x; - w=iw; - /* scan horizontal */ - while(w--) - { - /* invert pixels */ - ssd1306_drawPixel(m++, n, color); - } - n++; - } -} - -/* - * invert a rectangle in the buffer - */ -void ssd1306_xorrect(uint8_t x, uint8_t y, uint8_t w, uint8_t h) -{ - uint8_t m, n=y, iw = w; - - /* scan vertical */ - while(h--) - { - m=x; - w=iw; - /* scan horizontal */ - while(w--) - { - /* invert pixels */ - ssd1306_xorPixel(m++, n); - } - n++; - } -} - -/* - * Draw character to the display buffer - */ -void ssd1306_drawchar(uint8_t x, uint8_t y, uint8_t chr, uint8_t color) -{ - uint16_t i, j, col; - uint8_t d; - - for(i=0;i<8;i++) - { - d = fontdata[(chr<<3)+i]; - for(j=0;j<8;j++) - { - if(d&0x80) - col = color; - else - col = (~color)&1; - - ssd1306_drawPixel(x+j, y+i, col); - - // next bit - d <<= 1; - } - } -} - -/* - * draw a string to the display - */ -void ssd1306_drawstr(uint8_t x, uint8_t y, char *str, uint8_t color) -{ - uint8_t c; - - while((c=*str++)) - { - ssd1306_drawchar(x, y, c, color); - x += 8; - if(x>120) - break; - } -} - -/* - * enum for font size - */ -typedef enum { - fontsize_8x8 = 1, - fontsize_16x16 = 2, - fontsize_32x32 = 4, - fontsize_64x64 = 8, -} font_size_t; - -/* - * Draw character to the display buffer, scaled to size - */ -void ssd1306_drawchar_sz(uint8_t x, uint8_t y, uint8_t chr, uint8_t color, font_size_t font_size) -{ - uint16_t i, j, col; - uint8_t d; - - // Determine the font scale factor based on the font_size parameter - uint8_t font_scale = (uint8_t)font_size; - - // Loop through each row of the font data - for (i = 0; i < 8; i++) - { - // Retrieve the font data for the current row - d = fontdata[(chr << 3) + i]; - - // Loop through each column of the font data - for (j = 0; j < 8; j++) - { - // Determine the color to draw based on the current bit in the font data - if (d & 0x80) - col = color; - else - col = (~color) & 1; - - // Draw the pixel at the original size and scaled size using nested for-loops - for (uint8_t k = 0; k < font_scale; k++) { - for (uint8_t l = 0; l < font_scale; l++) { - ssd1306_drawPixel(x + (j * font_scale) + k, y + (i * font_scale) + l, col); - } - } - - // Move to the next bit in the font data - d <<= 1; - } - } -} - -/* - * draw a string to the display buffer, scaled to size - */ -void ssd1306_drawstr_sz(uint8_t x, uint8_t y, char *str, uint8_t color, font_size_t font_size) -{ - uint8_t c; - - while((c=*str++)) - { - ssd1306_drawchar_sz(x, y, c, color, font_size); - x += 8 * font_size; - if(x>128 - 8 * font_size) - break; - } -} - -/* - * initialize I2C and OLED - */ -uint8_t ssd1306_init(void) -{ - // pulse reset - ssd1306_rst(); - - // initialize OLED - uint8_t *cmd_list = (uint8_t *)ssd1306_init_array; - while(*cmd_list != SSD1306_TERMINATE_CMDS) - { - if(ssd1306_cmd(*cmd_list++)) - return 1; - } - - // clear display - ssd1306_setbuf(0); - ssd1306_refresh(); - - return 0; -} - -#endif diff --git a/ch32v/lib/ssd1306_i2c.h b/ch32v/lib/ssd1306_i2c.h deleted file mode 100644 index 1e96145..0000000 --- a/ch32v/lib/ssd1306_i2c.h +++ /dev/null @@ -1,374 +0,0 @@ -/* - * Single-File-Header for SSD1306 I2C interface - * 05-07-2023 E. Brombaugh - */ - -#ifndef _SSD1306_I2C_H -#define _SSD1306_I2C_H - -#include - -// SSD1306 I2C address -#define SSD1306_I2C_ADDR 0x3c - -// I2C Bus clock rate - must be lower the Logic clock rate -#define SSD1306_I2C_CLKRATE 1000000 - -// I2C Logic clock rate - must be higher than Bus clock rate -#define SSD1306_I2C_PRERATE 2000000 - -// uncomment this for high-speed 36% duty cycle, otherwise 33% -#define SSD1306_I2C_DUTY - -// I2C Timeout count -#define TIMEOUT_MAX 100000 - -// uncomment this to enable IRQ-driven operation -//#define SSD1306_I2C_IRQ - -#ifdef SSD1306_I2C_IRQ -// some stuff that IRQ mode needs -volatile uint8_t ssd1306_i2c_send_buffer[64], *ssd1306_i2c_send_ptr, ssd1306_i2c_send_sz, ssd1306_i2c_irq_state; - -// uncomment this to enable time diags in IRQ -//#define IRQ_DIAG -#endif - -/* - * init just I2C - */ -void ssd1306_i2c_setup(void) -{ - uint16_t tempreg; - - // Reset I2C1 to init all regs - RCC->APB1PRSTR |= RCC_APB1Periph_I2C1; - RCC->APB1PRSTR &= ~RCC_APB1Periph_I2C1; - - // set freq - tempreg = I2C1->CTLR2; - tempreg &= ~I2C_CTLR2_FREQ; - tempreg |= (FUNCONF_SYSTEM_CORE_CLOCK/SSD1306_I2C_PRERATE)&I2C_CTLR2_FREQ; - I2C1->CTLR2 = tempreg; - - // Set clock config - tempreg = 0; -#if (SSD1306_I2C_CLKRATE <= 100000) - // standard mode good to 100kHz - tempreg = (FUNCONF_SYSTEM_CORE_CLOCK/(2*SSD1306_I2C_CLKRATE))&SSD1306_I2C_CKCFGR_CCR; -#else - // fast mode over 100kHz -#ifndef SSD1306_I2C_DUTY - // 33% duty cycle - tempreg = (FUNCONF_SYSTEM_CORE_CLOCK/(3*SSD1306_I2C_CLKRATE))&SSD1306_I2C_CKCFGR_CCR; -#else - // 36% duty cycle - tempreg = (FUNCONF_SYSTEM_CORE_CLOCK/(25*SSD1306_I2C_CLKRATE))&I2C_CKCFGR_CCR; - tempreg |= I2C_CKCFGR_DUTY; -#endif - tempreg |= I2C_CKCFGR_FS; -#endif - I2C1->CKCFGR = tempreg; - -#ifdef SSD1306_I2C_IRQ - // enable IRQ driven operation - NVIC_EnableIRQ(I2C1_EV_IRQn); - - // initialize the state - ssd1306_i2c_irq_state = 0; -#endif - - // Enable I2C - I2C1->CTLR1 |= I2C_CTLR1_PE; - - // set ACK mode - I2C1->CTLR1 |= I2C_CTLR1_ACK; -} - -/* - * error descriptions - */ -char *errstr[] = -{ - "not busy", - "master mode", - "transmit mode", - "tx empty", - "transmit complete", -}; - -/* - * error handler - */ -uint8_t ssd1306_i2c_error(uint8_t err) -{ - // report error - printf("ssd1306_i2c_error - timeout waiting for %s\n\r", errstr[err]); - - // reset & initialize I2C - ssd1306_i2c_setup(); - - return 1; -} - -// event codes we use -#define SSD1306_I2C_EVENT_MASTER_MODE_SELECT ((uint32_t)0x00030001) /* BUSY, MSL and SB flag */ -#define SSD1306_I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ((uint32_t)0x00070082) /* BUSY, MSL, ADDR, TXE and TRA flags */ -#define SSD1306_I2C_EVENT_MASTER_BYTE_TRANSMITTED ((uint32_t)0x00070084) /* TRA, BUSY, MSL, TXE and BTF flags */ - -/* - * check for 32-bit event codes - */ -uint8_t ssd1306_i2c_chk_evt(uint32_t event_mask) -{ - /* read order matters here! STAR1 before STAR2!! */ - uint32_t status = I2C1->STAR1 | (I2C1->STAR2<<16); - return (status & event_mask) == event_mask; -} - -#ifdef SSD1306_I2C_IRQ -/* - * packet send for IRQ-driven operation - */ -uint8_t ssd1306_i2c_send(uint8_t addr, uint8_t *data, uint8_t sz) -{ - int32_t timeout; - -#ifdef IRQ_DIAG - GPIOC->BSHR = (1<<(3)); -#endif - - // error out if buffer under/overflow - if((sz > sizeof(ssd1306_i2c_send_buffer)) || !sz) - return 2; - - // wait for previous packet to finish - while(ssd1306_i2c_irq_state); - -#ifdef IRQ_DIAG - GPIOC->BSHR = (1<<(16+3)); - GPIOC->BSHR = (1<<(4)); -#endif - - // init buffer for sending - ssd1306_i2c_send_sz = sz; - ssd1306_i2c_send_ptr = ssd1306_i2c_send_buffer; - memcpy((uint8_t *)ssd1306_i2c_send_buffer, data, sz); - - // wait for not busy - timeout = TIMEOUT_MAX; - while((I2C1->STAR2 & I2C_STAR2_BUSY) && (timeout--)); - if(timeout==-1) - return ssd1306_i2c_error(0); - - // Set START condition - I2C1->CTLR1 |= I2C_CTLR1_START; - - // wait for master mode select - timeout = TIMEOUT_MAX; - while((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_MODE_SELECT)) && (timeout--)); - if(timeout==-1) - return ssd1306_i2c_error(1); - - // send 7-bit address + write flag - I2C1->DATAR = addr<<1; - - // wait for transmit condition - timeout = TIMEOUT_MAX; - while((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) && (timeout--)); - if(timeout==-1) - return ssd1306_i2c_error(2); - - // Enable TXE interrupt - I2C1->CTLR2 |= I2C_CTLR2_ITBUFEN | I2C_CTLR2_ITEVTEN; - ssd1306_i2c_irq_state = 1; - -#ifdef IRQ_DIAG - GPIOC->BSHR = (1<<(16+4)); -#endif - - // exit - return 0; -} - -/* - * IRQ handler for I2C events - */ -void I2C1_EV_IRQHandler(void) __attribute__((interrupt)); -void I2C1_EV_IRQHandler(void) -{ - uint16_t STAR1, STAR2 __attribute__((unused)); - -#ifdef IRQ_DIAG - GPIOC->BSHR = (1<<(4)); -#endif - - // read status, clear any events - STAR1 = I2C1->STAR1; - STAR2 = I2C1->STAR2; - - /* check for TXE */ - if(STAR1 & I2C_STAR1_TXE) - { - /* check for remaining data */ - if(ssd1306_i2c_send_sz--) - I2C1->DATAR = *ssd1306_i2c_send_ptr++; - - /* was that the last byte? */ - if(!ssd1306_i2c_send_sz) - { - // disable TXE interrupt - I2C1->CTLR2 &= ~(I2C_CTLR2_ITBUFEN | I2C_CTLR2_ITEVTEN); - - // reset IRQ state - ssd1306_i2c_irq_state = 0; - - // wait for tx complete - while(!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_BYTE_TRANSMITTED)); - - // set STOP condition - I2C1->CTLR1 |= I2C_CTLR1_STOP; - } - } - -#ifdef IRQ_DIAG - GPIOC->BSHR = (1<<(16+4)); -#endif -} -#else -/* - * low-level packet send for blocking polled operation via i2c - */ -uint8_t ssd1306_i2c_send(uint8_t addr, uint8_t *data, uint8_t sz) -{ - int32_t timeout; - - // wait for not busy - timeout = TIMEOUT_MAX; - while((I2C1->STAR2 & I2C_STAR2_BUSY) && (timeout--)); - if(timeout==-1) - return ssd1306_i2c_error(0); - - // Set START condition - I2C1->CTLR1 |= I2C_CTLR1_START; - - // wait for master mode select - timeout = TIMEOUT_MAX; - while((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_MODE_SELECT)) && (timeout--)); - if(timeout==-1) - return ssd1306_i2c_error(1); - - // send 7-bit address + write flag - I2C1->DATAR = addr<<1; - - // wait for transmit condition - timeout = TIMEOUT_MAX; - while((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) && (timeout--)); - if(timeout==-1) - return ssd1306_i2c_error(2); - - // send data one byte at a time - while(sz--) - { - // wait for TX Empty - timeout = TIMEOUT_MAX; - while(!(I2C1->STAR1 & I2C_STAR1_TXE) && (timeout--)); - if(timeout==-1) - return ssd1306_i2c_error(3); - - // send command - I2C1->DATAR = *data++; - } - - // wait for tx complete - timeout = TIMEOUT_MAX; - while((!ssd1306_i2c_chk_evt(SSD1306_I2C_EVENT_MASTER_BYTE_TRANSMITTED)) && (timeout--)); - if(timeout==-1) - return ssd1306_i2c_error(4); - - // set STOP condition - I2C1->CTLR1 |= I2C_CTLR1_STOP; - - // we're happy - return 0; -} -#endif - -/* - * high-level packet send for I2C - */ -uint8_t ssd1306_pkt_send(uint8_t *data, uint8_t sz, uint8_t cmd) -{ - uint8_t pkt[33]; - - /* build command or data packets */ - if(cmd) - { - pkt[0] = 0; - pkt[1] = *data; - } - else - { - pkt[0] = 0x40; - memcpy(&pkt[1], data, sz); - } - return ssd1306_i2c_send(SSD1306_I2C_ADDR, pkt, sz+1); -} - -/* - * init I2C and GPIO - */ -uint8_t ssd1306_i2c_init(void) -{ - // Enable GPIOC and I2C - RCC->APB1PCENR |= RCC_APB1Periph_I2C1; - -#ifdef CH32V20x - RCC->APB2PCENR |= RCC_APB2Periph_GPIOB; - // PB7 is SDA, 10MHz Output, alt func, open-drain - GPIOB->CFGLR &= ~(0xf<<(4*7)); - GPIOB->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_OD_AF)<<(4*7); - - // PB6 is SCL, 10MHz Output, alt func, open-drain - GPIOB->CFGLR &= ~(0xf<<(4*6)); - GPIOB->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_OD_AF)<<(4*6); -#else - RCC->APB2PCENR |= RCC_APB2Periph_GPIOC; - // PC1 is SDA, 10MHz Output, alt func, open-drain - GPIOC->CFGLR &= ~(0xf<<(4*1)); - GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_OD_AF)<<(4*1); - - // PC2 is SCL, 10MHz Output, alt func, open-drain - GPIOC->CFGLR &= ~(0xf<<(4*2)); - GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_OD_AF)<<(4*2); -#endif - -#ifdef IRQ_DIAG - // GPIO diags on PC3/PC4 - GPIOC->CFGLR &= ~(0xf<<(4*3)); - GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*3); - GPIOC->BSHR = (1<<(16+3)); - GPIOC->CFGLR &= ~(0xf<<(4*4)); - GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP)<<(4*4); - GPIOC->BSHR = (1<<(16+4)); -#endif - - // load I2C regs - ssd1306_i2c_setup(); - -#if 0 - // test if SSD1306 is on the bus by sending display off command - uint8_t command = 0xAF; - return ssd1306_pkt_send(&command, 1, 1); -#else - return 0; -#endif -} - -/* - * reset is not used for SSD1306 I2C interface - */ -void ssd1306_rst(void) -{ -} -#endif diff --git a/ch32v/lib/webhidcontrol.js b/ch32v/lib/webhidcontrol.js new file mode 100644 index 0000000..ca05975 --- /dev/null +++ b/ch32v/lib/webhidcontrol.js @@ -0,0 +1,226 @@ +const expectedProductName = "CNLohr lolra ch32v203 goertzel test"; +const filter = { vendorId : 0x1209, productId : 0xd035 }; +let dev = null; +let loopAbort = false; + +function setStatus( msg ) +{ + document.getElementById( "STATUS" ).innerHTML = msg; + console.log( msg ); +} + +function setStatusError( msg ) +{ + setStatus( "" + msg + "" ); + 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" ); +} + +function onLoadWebHidControl() +{ + 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 ); +} + +async function sendLoop() +{ + const sleep = ms => new Promise(r => setTimeout(r, ms)); + var arraySend = new Uint8Array(255); + var frameNo = 0|0; + var lastTime = performance.now(); + let goodCount = 0; + let badCount = 0; + let kBsecAvg = 0; + let xActionSecAvg = 0; + while( true ) + { + if( dev && !loopAbort ) + { + var i = 0|0; + for( var i = 0|0; i < 255|0; i++ ) + arraySend[i] = (Math.random()*256)|0; + + sendReport = dev.sendFeatureReport( 0xAA, arraySend ).catch( sendLoopError ); + if( !sendReport ) sendLoopError( "error creating sendFeatureReport" ); + + receiveReport = dev.receiveFeatureReport( 0xAA ).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 (Split between send and receive)
" + + (xActionSecAvg).toFixed(2) + "transactions/sec
" + + "Good Count: " + goodCount + "
Bad Count: " + badCount; + lastTime = thisTime; + } + + if( sendReport ) + { + await sendReport; + } + if( receiveReport ) + { + // Validate Data. + let receiveData = await receiveReport; + if( receiveData && receiveData.buffer ) + { + let data = new Uint8Array( receiveData.buffer ); + + // Tricky: Data goes: + // reportID -> Payload... + let failed = false; + for( var i = 0|0; i < 254|0; i++ ) + { + if( data[i+1] != arraySend[i] ) + { + console.log( "Disagreement at index " + i ); + console.log( data ); + console.log( arraySend ); + badCount++; + failed = true; + break; + } + } + if( !failed ) 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; + } + } + } + } +} +