diff --git a/ch32v/ch32v003-skitterrx/Makefile b/ch32v/ch32v003-skitterrx/Makefile new file mode 100644 index 0000000..5e2cdf7 --- /dev/null +++ b/ch32v/ch32v003-skitterrx/Makefile @@ -0,0 +1,18 @@ +all : flash + +TARGET:=adcrx +TARGET_MCU:=CH32V003 +CH32V003FUN:=../ch32v003fun/ch32v003fun + +ADDITIONAL_C_FILES+=../rv003usb/rv003usb/rv003usb.S ../rv003usb/rv003usb/rv003usb.c +EXTRA_CFLAGS:=-I../rv003usb/lib -I../rv003usb/rv003usb -mstrict-align -Wno-unused-function + +include ../ch32v003fun/ch32v003fun/ch32v003fun.mk + +flash : cv_flash +clean : cv_clean + rm -rf rf_data_gen chirpbuff.dat chirpbuff.h chirpbuffinfo.h + + + + diff --git a/ch32v/ch32v003-skitterrx/adcrx.c b/ch32v/ch32v003-skitterrx/adcrx.c new file mode 100644 index 0000000..07f5a2e --- /dev/null +++ b/ch32v/ch32v003-skitterrx/adcrx.c @@ -0,0 +1,387 @@ +/** + +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. + + +#include "ch32v003fun.h" +#include +#include +#include "rv003usb.h" + +uint8_t scratchout[15]; +volatile int outready = 0; +uint8_t scratchin[255]; +volatile int inready = 0; + + +#define PWM_PERIOD (28-1) //For 27.000500MHz +#define QUADRATURE + +uint32_t TQ = 128; + +#define ADC_BUFFSIZE 256 +volatile uint16_t adc_buffer[ADC_BUFFSIZE]; + +void SetupADC() +{ + + // 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 = 6; // 0-9 for 8 ext inputs and two internals /// 7 or 6 means one of the ADC inputs. + + // Not using injection group. + + // PD4 is analog input chl 7 + 6 + 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. + // 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_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_EXTSEL; //ADC_CONT + + // start conversion + ADC1->CTLR2 |= ADC_SWSTART; + +} + +static void SetupTimer1() +{ + // Enable Timer 1 + RCC->APB2PRSTR |= RCC_APB2Periph_TIM1; + RCC->APB2PRSTR &= ~RCC_APB2Periph_TIM1; + + TIM1->PSC = 0x0000; // Prescalar to 0x0000 (so, 48MHz base clock) + TIM1->ATRLR = PWM_PERIOD; + +#ifdef PWM_OUTPUT + GPIOC->CFGLR &= ~(0xf<<(4*4)); + GPIOC->CFGLR |= (GPIO_Speed_10MHz | GPIO_CNF_OUT_PP_AF)<<(4*4); + + TIM1->CCER = TIM_CC4E | TIM_CC4P; + TIM1->CHCTLR2 = TIM_OC4M_2 | TIM_OC4M_1; + TIM1->CH4CVR = 5; // Actual duty cycle (Off to begin with) +#endif + + // Setup TRGO for ADC. This makes is to the ADC will trigger on timer + // reset, so we trigger at the same position every time relative to the + // FET turning on. + 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; + int Q = TQ; + + // 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; + +#ifdef DUMPBUFF + uint16_t shadowbuff[Q+16]; + int shadowplace = 0; + #define SHADOWSTORE(X) shadowbuff[frcnt+X] = t; +#else + #define SHADOWSTORE(X) +#endif + + 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) + 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 ) + { + + int ti = i>>3; + int tq = q>>3; + int is = (ti*ti + tq*tq)>>8; + + int s = 1<<( ( 32 - __builtin_clz(is) )/2); + s = (s + is/s)/2; + + //int tv = (i>>PWM_OUTPUT) + (PWM_PERIOD/2); + //if( tv < 0 ) tv = 0; + //if( tv >= PWM_PERIOD ) tv = PWM_PERIOD-1; + //TIM1->CH4CVR = tv; + + //printf( "%d\n", s ); + 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() +{ + // REQUIRES External 24MHz oscillator + printf( "Initializing\n" ); + + SystemInit(); + + Delay_Ms(10); + + printf( "System On\n" ); + + // Enable Peripherals + RCC->APB2PCENR |= RCC_APB2Periph_GPIOD | RCC_APB2Periph_GPIOC | + RCC_APB2Periph_GPIOA | RCC_APB2Periph_TIM1 | RCC_APB2Periph_ADC1 | + RCC_APB2Periph_AFIO; + + RCC->APB1PCENR = RCC_APB1Periph_TIM2; + + // Disable HSI + RCC->CTLR &= ~(RCC_HSION); + + printf( "CTLR: %08lx CFGR0: %08lx\n", RCC->CTLR, RCC->CFGR0 ); + + SetupADC(); + + +#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(); +} + + + + + + +void usb_handle_user_in_request( struct usb_endpoint * e, uint8_t * scratchpad, int endp, uint32_t sendtok, struct rv003usb_internal * ist ) +{ + // Make sure we only deal with control messages. Like get/set feature reports. + if( endp ) + { + usb_send_empty( sendtok ); + } +} + +void usb_handle_user_data( struct usb_endpoint * e, int current_endpoint, uint8_t * data, int len, struct rv003usb_internal * ist ) +{ + if( outready ) + { + // Send NACK (can't accept any more data right now) + usb_send_data( 0, 0, 2, 0x5A ); + return; + } + + usb_send_data( 0, 0, 2, 0xD2 ); // Send ACK + int offset = e->count<<3; + int torx = e->max_len - offset; + if( torx > len ) torx = len; + if( torx > 0 ) + { + memcpy( scratchout + offset, data, torx ); + e->count++; + if( ( e->count << 3 ) >= e->max_len ) + { + outready = e->max_len; + } + } +} + + +void usb_handle_hid_get_report_start( struct usb_endpoint * e, int reqLen, uint32_t lValueLSBIndexMSB ) +{ + if( reqLen > sizeof( scratchin ) ) reqLen = sizeof( scratchin ); + + // You can check the lValueLSBIndexMSB word to decide what you want to do here + // But, whatever you point this at will be returned back to the host PC where + // it calls hid_get_feature_report. + // + // Please note, that on some systems, for this to work, your return length must + // match the length defined in HID_REPORT_COUNT, in your HID report, in usb_config.h + + if( reqLen > inready ) inready = inready; + e->opaque = scratchin; + e->max_len = reqLen; +} + +void usb_handle_hid_set_report_start( struct usb_endpoint * e, int reqLen, uint32_t lValueLSBIndexMSB ) +{ + // Here is where you get an alert when the host PC calls hid_send_feature_report. + // + // You can handle the appropriate message here. Please note that in this + // example, the data is chunked into groups-of-8-bytes. + // + // Note that you may need to make this match HID_REPORT_COUNT, in your HID + // report, in usb_config.h + + if( outready ) reqLen = 0; + if( reqLen > sizeof( scratchout ) ) reqLen = sizeof( scratchout ); + e->opaque = scratchout; + e->max_len = reqLen; +} + + +void usb_handle_other_control_message( struct usb_endpoint * e, struct usb_urb * s, struct rv003usb_internal * ist ) +{ + LogUEvent( SysTick->CNT, s->wRequestTypeLSBRequestMSB, s->lValueLSBIndexMSB, s->wLength ); +} + + + + + + + + + + diff --git a/ch32v/ch32v003-skitterrx/funconfig.h b/ch32v/ch32v003-skitterrx/funconfig.h new file mode 100644 index 0000000..eb91275 --- /dev/null +++ b/ch32v/ch32v003-skitterrx/funconfig.h @@ -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 + diff --git a/ch32v/ch32v003-skitterrx/sim/timtestskitter.ods b/ch32v/ch32v003-skitterrx/sim/timtestskitter.ods new file mode 100644 index 0000000..e69483c Binary files /dev/null and b/ch32v/ch32v003-skitterrx/sim/timtestskitter.ods differ diff --git a/ch32v/ch32v003-skitterrx/usb_config.h b/ch32v/ch32v003-skitterrx/usb_config.h new file mode 100644 index 0000000..f001937 --- /dev/null +++ b/ch32v/ch32v003-skitterrx/usb_config.h @@ -0,0 +1,155 @@ +#ifndef _USB_CONFIG_H +#define _USB_CONFIG_H + +//Defines the number of endpoints for this device. (Always add one for EP0). For two EPs, this should be 3. +#define ENDPOINTS 2 + +#define USB_PORT D // [A,C,D] GPIO Port to use with D+, D- and DPU +#define USB_PIN_DP 3 // [0-4] GPIO Number for USB D+ Pin +#define USB_PIN_DM 4 // [0-4] GPIO Number for USB D- Pin +#define USB_PIN_DPU 5 // [0-7] GPIO for feeding the 1.5k Pull-Up on USB D- Pin; Comment out if not used / tied to 3V3! + +#define RV003USB_DEBUG_TIMING 0 +#define RV003USB_OPTIMIZE_FLASH 1 +#define RV003USB_EVENT_DEBUGGING 0 +#define RV003USB_HANDLE_IN_REQUEST 1 +#define RV003USB_OTHER_CONTROL 0 +#define RV003USB_HANDLE_USER_DATA 1 +#define RV003USB_HID_FEATURES 1 +#define RV003USB_USER_DATA_HANDLES_TOKEN 1 + +#ifndef __ASSEMBLER__ + +#include + +#ifdef INSTANCE_DESCRIPTORS + +//Taken from http://www.usbmadesimple.co.uk/ums_ms_desc_dev.htm +static const uint8_t device_descriptor[] = { + 18, //Length + 1, //Type (Device) + 0x10, 0x01, //Spec + 0x0, //Device Class + 0x0, //Device Subclass + 0x0, //Device Protocol (000 = use config descriptor) + 0x08, //Max packet size for EP0 (This has to be 8 because of the USB Low-Speed Standard) + 0xcd, 0xab, //ID Vendor + 0x11, 0x11, //ID Product + 0x02, 0x00, //ID Rev + 1, //Manufacturer string + 2, //Product string + 3, //Serial string + 1, //Max number of configurations +}; + +static const uint8_t special_hid_desc[] = { + HID_USAGE_PAGE ( 0xff ), // Vendor-defined page. + HID_USAGE ( 0x00 ), + HID_REPORT_SIZE ( 8 ), + HID_COLLECTION ( HID_COLLECTION_LOGICAL ), + HID_REPORT_COUNT ( 255 ), // IN + HID_REPORT_ID ( 0xa4 ) + HID_USAGE ( 0x01 ), + HID_FEATURE ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ) , + HID_REPORT_COUNT_N ( 256, 2 ), // OUT + HID_REPORT_ID ( 0xad ) + HID_USAGE ( 0x01 ), + HID_COLLECTION_END, +}; + +static const uint8_t config_descriptor[] = { + // configuration descriptor, USB spec 9.6.3, page 264-266, Table 9-10 + 9, // bLength; + 2, // bDescriptorType; + 0x22, 0x00, // wTotalLength + + //34, 0x00, //for just the one descriptor + + 0x01, // bNumInterfaces (Normally 1) + 0x01, // bConfigurationValue + 0x00, // iConfiguration + 0x80, // bmAttributes (was 0xa0) + 0x64, // bMaxPower (200mA) + + //Class FF device. + 9, // bLength + 4, // bDescriptorType + 0, // bInterfaceNumber = 1 instead of 0 -- well make it second. + 0, // bAlternateSetting + 1, // bNumEndpoints + 0x03, // bInterfaceClass (0x03 = HID) + 0x00, // bInterfaceSubClass + 0xff, // bInterfaceProtocol (1 = Keyboard, 2 = Mouse) + 0, // iInterface + + 9, // bLength + 0x21, // bDescriptorType (HID) + 0x10,0x01, // bcd 1.1 + 0x00, //country code + 0x01, // Num descriptors + 0x22, // DescriptorType[0] (HID) + sizeof(special_hid_desc), 0x00, + + 7, // endpoint descriptor (For endpoint 1) + 0x05, // Endpoint Descriptor (Must be 5) + 0x81, // Endpoint Address + 0x03, // Attributes + 0x01, 0x00, // Size (We aren't using it) + 100, // Interval (We don't use it.) +}; + +#define STR_MANUFACTURER u"CNLohr" +#define STR_PRODUCT u"RV003 RVSWDIO Programmer" +#define STR_SERIAL u"RVSWDIO003-01" + +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, special_hid_desc, sizeof(special_hid_desc)}, + {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 // INSTANCE_DESCRIPTORS + +#endif + +#endif