Implement full-blown NCO using DMA sniffer
This commit is contained in:
		
							parent
							
								
									f866c97fcb
								
							
						
					
					
						commit
						49cf85006d
					
				
					 1 changed files with 82 additions and 58 deletions
				
			
		
							
								
								
									
										140
									
								
								src/main.c
									
									
									
									
									
								
							
							
						
						
									
										140
									
								
								src/main.c
									
									
									
									
									
								
							|  | @ -24,6 +24,10 @@ | |||
| #define VREG_VOLTAGE VREG_VOLTAGE_1_20 | ||||
| #define CLK_SYS_HZ (288 * MHZ) | ||||
| 
 | ||||
| #define INIT_SAMPLE_RATE 100000 | ||||
| #define INIT_FREQ 94600000 | ||||
| #define INIT_GAIN 127 | ||||
| 
 | ||||
| #define LO_PIN 9 | ||||
| #define RX_PIN 13 | ||||
| #define FB_PIN 5 | ||||
|  | @ -39,19 +43,23 @@ | |||
| #define IQ_BLOCK_LEN (2 * IQ_SAMPLES) | ||||
| #define IQ_QUEUE_LEN 8 | ||||
| 
 | ||||
| #define LO_NUM_PHASES (1 << 6) | ||||
| #define LO_PHASE_BITS 10 | ||||
| #define LO_PHASE_WORDS (1 << (LO_PHASE_BITS - 2)) | ||||
| #define LO_COS_PHASES (1 << 14) | ||||
| #define LO_EFFECTIVE_BITS (32 * LO_PHASE_WORDS * LO_COS_PHASES) | ||||
| /*
 | ||||
|  * NOTE: Must have 256 phases with 256 bytes each. | ||||
|  *       Otherwise the DMA 1-byte write trick wouldn't work. | ||||
|  */ | ||||
| 
 | ||||
| const double step_hz = (double)CLK_SYS_HZ / (LO_EFFECTIVE_BITS / 2.0); | ||||
| #define LO_NUM_PHASES 256 | ||||
| #define LO_PHASE_BITS 8 | ||||
| #define LO_PHASE_WORDS (1 << (LO_PHASE_BITS - 2)) | ||||
| #define STEP_BASE ((UINT_MAX + 1.0) / CLK_SYS_HZ) | ||||
| 
 | ||||
| static uint32_t nco_step = (uint32_t)(STEP_BASE * INIT_FREQ) * 32 * LO_PHASE_WORDS; | ||||
| static uint32_t nco_null = 0; | ||||
| 
 | ||||
| static uint32_t lo_phase[LO_NUM_PHASES][LO_PHASE_WORDS] | ||||
| 	__attribute__((__aligned__(LO_NUM_PHASES * 4 * LO_PHASE_WORDS))); | ||||
| 
 | ||||
| static const uint32_t *lo_cos_phases[LO_COS_PHASES] | ||||
| 	__attribute__((__aligned__(1 << LO_PHASE_BITS))); | ||||
| static uint32_t nco_addr = (uint32_t)lo_phase; | ||||
| 
 | ||||
| #define DECIMATE 16 | ||||
| #define RX_BITS_DEPTH 10 | ||||
|  | @ -62,10 +70,6 @@ static uint32_t rx_cos[RX_WORDS] __attribute__((__aligned__(1 << RX_BITS_DEPTH)) | |||
| static const uint32_t *rx_start = rx_cos; | ||||
| static const uint32_t *rx_end = rx_cos + RX_WORDS - 1; | ||||
| 
 | ||||
| #define INIT_SAMPLE_RATE 100000 | ||||
| #define INIT_FREQ 94600000 | ||||
| #define INIT_GAIN 127 | ||||
| 
 | ||||
| #define NUM_GAINS 29 | ||||
| static int gains[NUM_GAINS] = { 0,   9,	  14,  27,  37,	 77,  87,  125, 144, 157, | ||||
| 				166, 197, 207, 229, 254, 280, 297, 328, 338, 364, | ||||
|  | @ -78,8 +82,10 @@ static int frequency = INIT_FREQ; | |||
| static int dma_ch_rx1 = -1; | ||||
| static int dma_ch_rx2 = -1; | ||||
| 
 | ||||
| static int dma_ch_mix1 = -1; | ||||
| static int dma_ch_mix2 = -1; | ||||
| static int dma_ch_nco1 = -1; | ||||
| static int dma_ch_nco2 = -1; | ||||
| static int dma_ch_nco3 = -1; | ||||
| static int dma_ch_mix = -1; | ||||
| 
 | ||||
| static int dma_ch_samp_cos = -1; | ||||
| 
 | ||||
|  | @ -224,6 +230,8 @@ static void init_rx() | |||
| 	pio_sm_set_consecutive_pindirs(PIO, SM_RX, RX_PIN, 1, GPIO_IN); | ||||
| } | ||||
| 
 | ||||
| static const uint32_t samp_insn = 16; | ||||
| 
 | ||||
| static void init_ad() | ||||
| { | ||||
| 	const uint16_t insn[] = { | ||||
|  | @ -277,8 +285,6 @@ static void init_ad() | |||
| 	pio_sm_init(PIO, SM_AD, origin_ad, &pc); | ||||
| } | ||||
| 
 | ||||
| #define STEP_BASE ((UINT_MAX + 1.0) / CLK_SYS_HZ) | ||||
| 
 | ||||
| static void lo_generate_phase(uint32_t *buf, size_t len, uint32_t step, uint32_t phase) | ||||
| { | ||||
| 	for (size_t i = 0; i < len; i++) { | ||||
|  | @ -294,37 +300,25 @@ static void lo_generate_phase(uint32_t *buf, size_t len, uint32_t step, uint32_t | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void rx_lo_init(double req_freq, bool align) | ||||
| static void rx_lo_init(double freq) | ||||
| { | ||||
| 	double freq = req_freq; | ||||
| 
 | ||||
| 	if (align) | ||||
| 		freq = round(freq / step_hz) * step_hz; | ||||
| 
 | ||||
| 	uint32_t step = STEP_BASE * freq; | ||||
| 
 | ||||
| 	for (uint32_t i = 0; i < LO_NUM_PHASES; i++) | ||||
| 		lo_generate_phase(lo_phase[i], LO_PHASE_WORDS, step, | ||||
| 				  i << (__builtin_clz(LO_NUM_PHASES) + 1)); | ||||
| 		lo_generate_phase(lo_phase[i], LO_PHASE_WORDS, step, i << 24); | ||||
| 
 | ||||
| 	uint32_t phase_step = step * 32 * LO_PHASE_WORDS; | ||||
| 	uint32_t phase = 0; | ||||
| 
 | ||||
| 	for (uint32_t i = 0; i < LO_COS_PHASES; i++) { | ||||
| 		lo_cos_phases[i] = lo_phase[phase >> (__builtin_clz(LO_NUM_PHASES) + 1)]; | ||||
| 		phase += phase_step; | ||||
| 	} | ||||
| 	nco_step = step * 32 * LO_PHASE_WORDS; | ||||
| } | ||||
| 
 | ||||
| static const uint32_t samp_insn = 16; | ||||
| 
 | ||||
| static void rf_rx_start() | ||||
| { | ||||
| 	dma_ch_rx1 = dma_claim_unused_channel(true); | ||||
| 	dma_ch_rx2 = dma_claim_unused_channel(true); | ||||
| 
 | ||||
| 	dma_ch_mix1 = dma_claim_unused_channel(true); | ||||
| 	dma_ch_mix2 = dma_claim_unused_channel(true); | ||||
| 	dma_ch_nco1 = dma_claim_unused_channel(true); | ||||
| 	dma_ch_nco2 = dma_claim_unused_channel(true); | ||||
| 	dma_ch_nco3 = dma_claim_unused_channel(true); | ||||
| 	dma_ch_mix = dma_claim_unused_channel(true); | ||||
| 
 | ||||
| 	dma_ch_samp_cos = dma_claim_unused_channel(true); | ||||
| 
 | ||||
|  | @ -349,22 +343,42 @@ static void rf_rx_start() | |||
| 	dma_channel_configure(dma_ch_rx2, &dma_conf, &PIO->txf[SM_AD], &PIO->rxf[SM_RX], UINT_MAX, | ||||
| 			      false); | ||||
| 
 | ||||
| 	/* Drive the LO capacitor. */ | ||||
| 	dma_conf = dma_channel_get_default_config(dma_ch_mix1); | ||||
| 	/* Step the NCO. */ | ||||
| 	dma_conf = dma_channel_get_default_config(dma_ch_nco1); | ||||
| 	channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32); | ||||
| 	channel_config_set_read_increment(&dma_conf, true); | ||||
| 	channel_config_set_read_increment(&dma_conf, false); | ||||
| 	channel_config_set_write_increment(&dma_conf, false); | ||||
| 	channel_config_set_ring(&dma_conf, GPIO_IN, LO_PHASE_BITS); | ||||
| 	dma_channel_configure(dma_ch_mix1, &dma_conf, &dma_hw->ch[dma_ch_mix2].al3_read_addr_trig, | ||||
| 			      lo_cos_phases, 1, false); | ||||
| 	channel_config_set_chain_to(&dma_conf, dma_ch_nco2); | ||||
| 	dma_channel_configure(dma_ch_nco1, &dma_conf, &nco_null, &nco_step, 1, false); | ||||
| 
 | ||||
| 	dma_conf = dma_channel_get_default_config(dma_ch_mix2); | ||||
| 	/* DMA above will increment the phase accumulator. */ | ||||
| 	dma_sniffer_enable(dma_ch_nco1, DMA_SNIFF_CTRL_CALC_VALUE_SUM, true); | ||||
| 
 | ||||
| 	/* Prepare the phase address. */ | ||||
| 	dma_conf = dma_channel_get_default_config(dma_ch_nco2); | ||||
| 	channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_8); | ||||
| 	channel_config_set_read_increment(&dma_conf, false); | ||||
| 	channel_config_set_write_increment(&dma_conf, false); | ||||
| 	channel_config_set_chain_to(&dma_conf, dma_ch_nco3); | ||||
| 	dma_channel_configure(dma_ch_nco2, &dma_conf, (uint8_t *)(&nco_addr) + 1, | ||||
| 			      ((uint8_t *)&dma_hw->sniff_data) + 3, 1, false); | ||||
| 
 | ||||
| 	/* Trigger LO using the address. */ | ||||
| 	dma_conf = dma_channel_get_default_config(dma_ch_nco3); | ||||
| 	channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32); | ||||
| 	channel_config_set_read_increment(&dma_conf, false); | ||||
| 	channel_config_set_write_increment(&dma_conf, false); | ||||
| 	dma_channel_configure(dma_ch_nco3, &dma_conf, &dma_hw->ch[dma_ch_mix].al3_read_addr_trig, | ||||
| 			      &nco_addr, 1, false); | ||||
| 
 | ||||
| 	/* Drive the LO capacitor. */ | ||||
| 	dma_conf = dma_channel_get_default_config(dma_ch_mix); | ||||
| 	channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32); | ||||
| 	channel_config_set_read_increment(&dma_conf, true); | ||||
| 	channel_config_set_write_increment(&dma_conf, false); | ||||
| 	channel_config_set_dreq(&dma_conf, pio_get_dreq(PIO, SM_LO, GPIO_OUT)); | ||||
| 	channel_config_set_chain_to(&dma_conf, dma_ch_mix1); | ||||
| 	dma_channel_configure(dma_ch_mix2, &dma_conf, &PIO->txf[SM_LO], NULL, LO_PHASE_WORDS, | ||||
| 	channel_config_set_chain_to(&dma_conf, dma_ch_nco1); | ||||
| 	dma_channel_configure(dma_ch_mix, &dma_conf, &PIO->txf[SM_LO], lo_phase, LO_PHASE_WORDS, | ||||
| 			      false); | ||||
| 
 | ||||
| 	/* Trigger accumulator values push. */ | ||||
|  | @ -383,7 +397,7 @@ static void rf_rx_start() | |||
| 	init_rx(); | ||||
| 
 | ||||
| 	dma_channel_start(dma_ch_rx1); | ||||
| 	dma_channel_start(dma_ch_mix1); | ||||
| 	dma_channel_start(dma_ch_nco1); | ||||
| 	dma_channel_start(dma_ch_samp_cos); | ||||
| 
 | ||||
| 	pio_set_sm_mask_enabled(PIO, 0x0f, true); | ||||
|  | @ -397,32 +411,42 @@ static void rf_rx_stop(void) | |||
| 
 | ||||
| 	dma_channel_clear_chain_to(dma_ch_rx1); | ||||
| 	dma_channel_clear_chain_to(dma_ch_rx2); | ||||
| 	dma_channel_clear_chain_to(dma_ch_mix1); | ||||
| 	dma_channel_clear_chain_to(dma_ch_mix2); | ||||
| 	dma_channel_clear_chain_to(dma_ch_nco1); | ||||
| 	dma_channel_clear_chain_to(dma_ch_nco2); | ||||
| 	dma_channel_clear_chain_to(dma_ch_nco3); | ||||
| 	dma_channel_clear_chain_to(dma_ch_mix); | ||||
| 	dma_channel_clear_chain_to(dma_ch_samp_cos); | ||||
| 
 | ||||
| 	dma_channel_abort(dma_ch_rx1); | ||||
| 	dma_channel_abort(dma_ch_rx2); | ||||
| 	dma_channel_abort(dma_ch_mix1); | ||||
| 	dma_channel_abort(dma_ch_mix2); | ||||
| 	dma_channel_abort(dma_ch_nco1); | ||||
| 	dma_channel_abort(dma_ch_nco2); | ||||
| 	dma_channel_abort(dma_ch_nco3); | ||||
| 	dma_channel_abort(dma_ch_mix); | ||||
| 	dma_channel_abort(dma_ch_samp_cos); | ||||
| 
 | ||||
| 	dma_channel_cleanup(dma_ch_rx1); | ||||
| 	dma_channel_cleanup(dma_ch_rx2); | ||||
| 	dma_channel_cleanup(dma_ch_mix1); | ||||
| 	dma_channel_cleanup(dma_ch_mix2); | ||||
| 	dma_channel_cleanup(dma_ch_nco1); | ||||
| 	dma_channel_cleanup(dma_ch_nco2); | ||||
| 	dma_channel_cleanup(dma_ch_nco3); | ||||
| 	dma_channel_cleanup(dma_ch_mix); | ||||
| 	dma_channel_cleanup(dma_ch_samp_cos); | ||||
| 
 | ||||
| 	dma_channel_unclaim(dma_ch_rx1); | ||||
| 	dma_channel_unclaim(dma_ch_rx2); | ||||
| 	dma_channel_unclaim(dma_ch_mix1); | ||||
| 	dma_channel_unclaim(dma_ch_mix2); | ||||
| 	dma_channel_unclaim(dma_ch_nco1); | ||||
| 	dma_channel_unclaim(dma_ch_nco2); | ||||
| 	dma_channel_unclaim(dma_ch_nco3); | ||||
| 	dma_channel_unclaim(dma_ch_mix); | ||||
| 	dma_channel_unclaim(dma_ch_samp_cos); | ||||
| 
 | ||||
| 	dma_ch_rx1 = -1; | ||||
| 	dma_ch_rx2 = -1; | ||||
| 	dma_ch_mix1 = -1; | ||||
| 	dma_ch_mix2 = -1; | ||||
| 	dma_ch_nco1 = -1; | ||||
| 	dma_ch_nco2 = -1; | ||||
| 	dma_ch_nco3 = -1; | ||||
| 	dma_ch_mix = -1; | ||||
| 	dma_ch_samp_cos = -1; | ||||
| } | ||||
| 
 | ||||
|  | @ -572,13 +596,13 @@ static void run_command(uint8_t cmd, uint32_t arg) | |||
| 	if (0x01 == cmd) { | ||||
| 		/* Tune to a new center frequency */ | ||||
| 		frequency = arg; | ||||
| 		rx_lo_init(frequency + sample_rate, true); | ||||
| 		rx_lo_init(frequency + sample_rate); | ||||
| 	} else if (0x02 == cmd) { | ||||
| 		/* Set the rate at which IQ sample pairs are sent */ | ||||
| 		sample_rate = arg; | ||||
| 		dc_level = CLK_SYS_HZ / sample_rate / 2; | ||||
| 		dma_timer_set_fraction(dma_t_samp, 1, CLK_SYS_HZ / (sample_rate * DECIMATE)); | ||||
| 		rx_lo_init(frequency + sample_rate, true); | ||||
| 		rx_lo_init(frequency + sample_rate); | ||||
| 	} else if (0x04 == cmd) { | ||||
| 		/* Set the tuner gain level */ | ||||
| 		gain = INIT_GAIN * powf(10.0f, arg / 200.0f); | ||||
|  | @ -692,7 +716,7 @@ int main() | |||
| 
 | ||||
| 	queue_init(&iq_queue, sizeof(uint8_t *), IQ_QUEUE_LEN); | ||||
| 
 | ||||
| 	rx_lo_init(frequency + sample_rate, true); | ||||
| 	rx_lo_init(frequency + sample_rate); | ||||
| 
 | ||||
| 	dma_t_samp = dma_claim_unused_timer(true); | ||||
| 	dma_timer_set_fraction(dma_t_samp, 1, CLK_SYS_HZ / (sample_rate * DECIMATE)); | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue