diff --git a/ch32v/ch32v003-timer/Makefile b/ch32v/ch32v003-timer/Makefile new file mode 100644 index 0000000..0bd2881 --- /dev/null +++ b/ch32v/ch32v003-timer/Makefile @@ -0,0 +1,23 @@ +all : flash + +TARGET:=loratest +TARGET_MCU:=CH32V003 +CH32V003FUN:=../ch32v003fun/ch32v003fun + +EXTRA_ELF_DEPENDENCIES:=chirpbuff.h + +EXTRA_CFLAGS:=-Wno-unused-function -I../../lib + +include ../ch32v003fun/ch32v003fun/ch32v003fun.mk + + +chirpbuff.h : rf_data_gen + ./rf_data_gen + +rf_data_gen : rf_data_gen.c + gcc -o $@ $^ -lm + +flash : cv_flash +clean : cv_clean + rm -rf rf_data_gen chirpbuff.dat chirpbuff.h chirpbuffinfo.h + diff --git a/ch32v/ch32v003-timer/README.md b/ch32v/ch32v003-timer/README.md new file mode 100644 index 0000000..45ab432 --- /dev/null +++ b/ch32v/ch32v003-timer/README.md @@ -0,0 +1,9 @@ +# DMA Timer CH32V003 Firmware-Only 900MHz LoRa Transmitter + +THIS IS A DRAFT + +Be sure to provide a 24MHz clock (not crystal unless you disable the bypass) on PA1. + +Output will be on PC3 (T1C3) + +Generally you cannot use the built-in oscillator circuit becuase it's too spread-y. diff --git a/ch32v/ch32v003-timer/funconfig.h b/ch32v/ch32v003-timer/funconfig.h new file mode 100644 index 0000000..a2baf88 --- /dev/null +++ b/ch32v/ch32v003-timer/funconfig.h @@ -0,0 +1,9 @@ +#ifndef _FUNCONFIG_H +#define _FUNCONFIG_H + +#define FUNCONF_USE_DEBUGPRINTF 1 +#define FUNCONF_USE_UARTPRINTF 0 + + +#endif + diff --git a/ch32v/ch32v003-timer/loratest.c b/ch32v/ch32v003-timer/loratest.c new file mode 100644 index 0000000..096d07b --- /dev/null +++ b/ch32v/ch32v003-timer/loratest.c @@ -0,0 +1,477 @@ +/** + +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. + +**/ + +// 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 +#include +#include "chirpbuff.h" +#include "chirpbuffinfo.h" + +#include "LoRa-SDR-Code.h" + +//#define LORAWAN +//#define TEST_TONE + +#ifdef LORAWAN +#include "lorawan_simple.h" +#endif + +#define DMA_SIZE_WORDS 128 + +#define SENDBUFF_WORDS (DMA_SIZE_WORDS*2) +uint8_t sendbuff[SENDBUFF_WORDS]; + +#if !defined( FOUND_PERFECT_DIVISOR ) +#error Code only written for perfect division right now. +#endif + +// 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<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_Channel2->CFGR & 1 ); +// DMA1_Channel2->CNTR = DMA_SIZE_WORDS/2; +// DMA1_Channel2->MADDR = (uint32_t)tsb; +// DMA1_Channel2->PADDR = (uint32_t)sb; +// DMA1_Channel2->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 + +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 = 20; + + // Reload immediately + TIM1->SWEVGR |= TIM_UG; + + // Enable CH1N output, positive pol + TIM1->CCER |= TIM_CC3E; + TIM1->CCER |= TIM_CC1E; + + TIM1->CHCTLR2 |= TIM_OC3M_2 | TIM_OC3M_1; + TIM1->CHCTLR1 |= TIM_OC1M_2 | TIM_OC1M_1; + + // Set the Capture Compare Register value to 50% initially + TIM1->CH3CVR = 6; + TIM1->CH1CVR = 0; // This triggers DMA. + + // Enable TIM1 outputs + TIM1->BDTR |= TIM_MOE; + + // Enable TIM1 + TIM1->CTLR1 |= TIM_CEN; + + TIM1->DMAINTENR = TIM_TDE | TIM_COMDE | TIM_CC1DE | TIM_UDE; + + + //DMA1_Channel3 is for SPI1TX + DMA1_Channel2->PADDR = (uint32_t)&TIM1->ATRLR; + DMA1_Channel2->MADDR = (uint32_t)sendbuff; + DMA1_Channel2->CNTR = 0;// sizeof( bufferset )/2; // Number of unique copies. (Don't start, yet!) + DMA1_Channel2->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_Channel2->MADDR = (uint32_t)sendbuff; + DMA1_Channel2->CNTR = SENDBUFF_WORDS; // Number of unique uint16_t entries. + DMA1_Channel2->CFGR |= DMA_CFGR1_EN; + + int frameno = 0; + while(1) + { + //printf( "%08x %08x\n", TIM1->CH3CVR, TIM1->ATRLR ); + Delay_Ms( 1 ); + frameno++; + int j; + + int targ_f = (frameno & 127)*3 + 1370; + int run_f = 0; + for( j = 0; j < sizeof( sendbuff ); j++ ) + { + int setf = 12; + if( run_f > targ_f ) + { + setf = 11; + run_f -= targ_f; + } + run_f += setf*16; + + sendbuff[j] = setf; + } + } + +#if 0 + uint32_t frame = 0; + while(1) + { + Delay_Ms( 1000 ); + +#ifdef LORAWAN + // Optionally generate a LoRaWAN Packet. + const static uint8_t raw_pl[4] = { 0xaa, 0xaa, 0xaa, 0xaa, }; + // Just some random data. + static uint8_t raw_payload_with_b0[MAX_BYTES+8] = { 0 }; + uint8_t * payload_in = raw_payload_with_b0 + 16; + int payload_in_size; + + // Be sure to provide your appropriate keys and address here. + static const uint8_t payload_key[AES_BLOCKLEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // AppSKey Big Endian + static const uint8_t network_skey[AES_BLOCKLEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // NwkSKey Big Endian + static const uint8_t devaddress[4] = { 0x00, 0x00, 0x00, 0x00 }; // Device address Little Endian LSB (Written backwards from Device address default view) + + payload_in_size = GenerateLoRaWANPacket( raw_payload_with_b0, raw_pl, sizeof(raw_pl), payload_key, network_skey, devaddress, frame ); +#else + uint8_t payload_in[15] = { 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff }; + int payload_in_size = 15; + +#endif + + lora_symbols_count = 0; + int r = CreateMessageFromPayload( lora_symbols, &lora_symbols_count, MAX_SYMBOLS, SF_NUMBER, 4, payload_in, payload_in_size ); + + if( r < 0 ) + { + printf( "Failed to generate message (%d)\n", r ); + // Failed + continue; + } + + frame++; + + int j; + + quadsetcount = 0; + int16_t * qso = quadsets; + for( j = 0; j < PREAMBLE_CHIRPS; j++ ) + { + qso = AddChirp( qso, 0, 0 ); + } + + uint8_t syncword = 0x43; + + #if SF_NUMBER == 6 + #define CODEWORD_SHIFT 2 + #else + #define CODEWORD_SHIFT 3 + #endif + + if( CODEWORD_LENGTH > 0 ) + qso = AddChirp( qso, ( ( syncword & 0xf ) << CODEWORD_SHIFT ), 0 ); + if( CODEWORD_LENGTH > 1 ) + qso = AddChirp( qso, ( ( ( syncword & 0xf0 ) >> 4 ) << CODEWORD_SHIFT ), 0); + + *(qso++) = -(CHIPSSPREAD * 0 / 4 )-1; + *(qso++) = -(CHIPSSPREAD * 1 / 4 )-1; + *(qso++) = -(CHIPSSPREAD * 2 / 4 )-1; + *(qso++) = -(CHIPSSPREAD * 3 / 4 )-1; + *(qso++) = -(CHIPSSPREAD * 0 / 4 )-1; + *(qso++) = -(CHIPSSPREAD * 1 / 4 )-1; + *(qso++) = -(CHIPSSPREAD * 2 / 4 )-1; + *(qso++) = -(CHIPSSPREAD * 3 / 4 )-1; + *(qso++) = -(CHIPSSPREAD * 0 / 4 )-1; + + if( SF_NUMBER <= 6 ) + { + // Two additional upchirps with SF6 https://github.com/tapparelj/gr-lora_sdr/issues/74#issuecomment-1891569580 + for( j = 0; j < 2; j++ ) + qso = AddChirp( qso, 0, 0 ); + } + + for( j = 0; j < lora_symbols_count; j++ ) + { + int ofs = lora_symbols[j]; + qso = AddChirp( qso, ofs, 0 ); + } + + runningcount_bits = 0; + + // This tells the interrupt we have data. + quadsetcount = qso - quadsets + 0; + printf( "- %d -- %d [%d] %d; %d\n", (int)temp, (int)lora_symbols_count, (int)quadsetcount, CHIPSSPREAD/4, sendbuff[0] ); + quadsetplace = 0; + + DMA1_Channel3->CFGR |= DMA_CFGR1_EN; + + while( quadsetplace >= 0 ); + } +#endif +} + diff --git a/ch32v/ch32v003-timer/rf_data_gen.c b/ch32v/ch32v003-timer/rf_data_gen.c new file mode 100644 index 0000000..c75086e --- /dev/null +++ b/ch32v/ch32v003-timer/rf_data_gen.c @@ -0,0 +1,58 @@ +/* +MIT 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 following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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. +*/ + +#include + + +//#define KHZ500 + +#ifdef KHZ500 + +// Tested, works - be sure to set your radio to be able to receive this. +#define SF_NUMBER 10 +const double center_frequency = 904.6; +const double bw = .5; +#define SF_SYMBOL_TIME 0.00000025 +#else + +// Tested, works. +const double center_frequency = 904.5; +#define SF_NUMBER 8 +const double bw = .125; +#endif + +const uint32_t memory_offset = 0x00000; +#define SPI_DIV 2 +const double sample_rate = (48.0/2)/SPI_DIV; + + +#define ATTRIBUTE_FOR_DATA "__attribute__((section(\".text\"),aligned(32)))" +#define USE_16_BITS + +#include "../../lib/rf_data_gen.h" + +int main() +{ + return gen_buffer_files(); +}