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 VREG_VOLTAGE VREG_VOLTAGE_1_20 | ||||||
| #define CLK_SYS_HZ (288 * MHZ) | #define CLK_SYS_HZ (288 * MHZ) | ||||||
| 
 | 
 | ||||||
|  | #define INIT_SAMPLE_RATE 100000 | ||||||
|  | #define INIT_FREQ 94600000 | ||||||
|  | #define INIT_GAIN 127 | ||||||
|  | 
 | ||||||
| #define LO_PIN 9 | #define LO_PIN 9 | ||||||
| #define RX_PIN 13 | #define RX_PIN 13 | ||||||
| #define FB_PIN 5 | #define FB_PIN 5 | ||||||
|  | @ -39,19 +43,23 @@ | ||||||
| #define IQ_BLOCK_LEN (2 * IQ_SAMPLES) | #define IQ_BLOCK_LEN (2 * IQ_SAMPLES) | ||||||
| #define IQ_QUEUE_LEN 8 | #define IQ_QUEUE_LEN 8 | ||||||
| 
 | 
 | ||||||
| #define LO_NUM_PHASES (1 << 6) | /*
 | ||||||
| #define LO_PHASE_BITS 10 |  * NOTE: Must have 256 phases with 256 bytes each. | ||||||
| #define LO_PHASE_WORDS (1 << (LO_PHASE_BITS - 2)) |  *       Otherwise the DMA 1-byte write trick wouldn't work. | ||||||
| #define LO_COS_PHASES (1 << 14) |  */ | ||||||
| #define LO_EFFECTIVE_BITS (32 * LO_PHASE_WORDS * LO_COS_PHASES) |  | ||||||
| 
 | 
 | ||||||
| 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] | static uint32_t lo_phase[LO_NUM_PHASES][LO_PHASE_WORDS] | ||||||
| 	__attribute__((__aligned__(LO_NUM_PHASES * 4 * LO_PHASE_WORDS))); | 	__attribute__((__aligned__(LO_NUM_PHASES * 4 * LO_PHASE_WORDS))); | ||||||
| 
 | 
 | ||||||
| static const uint32_t *lo_cos_phases[LO_COS_PHASES] | static uint32_t nco_addr = (uint32_t)lo_phase; | ||||||
| 	__attribute__((__aligned__(1 << LO_PHASE_BITS))); |  | ||||||
| 
 | 
 | ||||||
| #define DECIMATE 16 | #define DECIMATE 16 | ||||||
| #define RX_BITS_DEPTH 10 | #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_start = rx_cos; | ||||||
| static const uint32_t *rx_end = rx_cos + RX_WORDS - 1; | 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 | #define NUM_GAINS 29 | ||||||
| static int gains[NUM_GAINS] = { 0,   9,	  14,  27,  37,	 77,  87,  125, 144, 157, | 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, | 				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_rx1 = -1; | ||||||
| static int dma_ch_rx2 = -1; | static int dma_ch_rx2 = -1; | ||||||
| 
 | 
 | ||||||
| static int dma_ch_mix1 = -1; | static int dma_ch_nco1 = -1; | ||||||
| static int dma_ch_mix2 = -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; | 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); | 	pio_sm_set_consecutive_pindirs(PIO, SM_RX, RX_PIN, 1, GPIO_IN); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static const uint32_t samp_insn = 16; | ||||||
|  | 
 | ||||||
| static void init_ad() | static void init_ad() | ||||||
| { | { | ||||||
| 	const uint16_t insn[] = { | 	const uint16_t insn[] = { | ||||||
|  | @ -277,8 +285,6 @@ static void init_ad() | ||||||
| 	pio_sm_init(PIO, SM_AD, origin_ad, &pc); | 	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) | 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++) { | 	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; | 	uint32_t step = STEP_BASE * freq; | ||||||
| 
 | 
 | ||||||
| 	for (uint32_t i = 0; i < LO_NUM_PHASES; i++) | 	for (uint32_t i = 0; i < LO_NUM_PHASES; i++) | ||||||
| 		lo_generate_phase(lo_phase[i], LO_PHASE_WORDS, step, | 		lo_generate_phase(lo_phase[i], LO_PHASE_WORDS, step, i << 24); | ||||||
| 				  i << (__builtin_clz(LO_NUM_PHASES) + 1)); |  | ||||||
| 
 | 
 | ||||||
| 	uint32_t phase_step = step * 32 * LO_PHASE_WORDS; | 	nco_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; |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static const uint32_t samp_insn = 16; |  | ||||||
| 
 |  | ||||||
| static void rf_rx_start() | static void rf_rx_start() | ||||||
| { | { | ||||||
| 	dma_ch_rx1 = dma_claim_unused_channel(true); | 	dma_ch_rx1 = dma_claim_unused_channel(true); | ||||||
| 	dma_ch_rx2 = dma_claim_unused_channel(true); | 	dma_ch_rx2 = dma_claim_unused_channel(true); | ||||||
| 
 | 
 | ||||||
| 	dma_ch_mix1 = dma_claim_unused_channel(true); | 	dma_ch_nco1 = dma_claim_unused_channel(true); | ||||||
| 	dma_ch_mix2 = 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); | 	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, | 	dma_channel_configure(dma_ch_rx2, &dma_conf, &PIO->txf[SM_AD], &PIO->rxf[SM_RX], UINT_MAX, | ||||||
| 			      false); | 			      false); | ||||||
| 
 | 
 | ||||||
| 	/* Drive the LO capacitor. */ | 	/* Step the NCO. */ | ||||||
| 	dma_conf = dma_channel_get_default_config(dma_ch_mix1); | 	dma_conf = dma_channel_get_default_config(dma_ch_nco1); | ||||||
| 	channel_config_set_transfer_data_size(&dma_conf, DMA_SIZE_32); | 	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_write_increment(&dma_conf, false); | ||||||
| 	channel_config_set_ring(&dma_conf, GPIO_IN, LO_PHASE_BITS); | 	channel_config_set_chain_to(&dma_conf, dma_ch_nco2); | ||||||
| 	dma_channel_configure(dma_ch_mix1, &dma_conf, &dma_hw->ch[dma_ch_mix2].al3_read_addr_trig, | 	dma_channel_configure(dma_ch_nco1, &dma_conf, &nco_null, &nco_step, 1, false); | ||||||
| 			      lo_cos_phases, 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_transfer_data_size(&dma_conf, DMA_SIZE_32); | ||||||
| 	channel_config_set_read_increment(&dma_conf, true); | 	channel_config_set_read_increment(&dma_conf, true); | ||||||
| 	channel_config_set_write_increment(&dma_conf, false); | 	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_dreq(&dma_conf, pio_get_dreq(PIO, SM_LO, GPIO_OUT)); | ||||||
| 	channel_config_set_chain_to(&dma_conf, dma_ch_mix1); | 	channel_config_set_chain_to(&dma_conf, dma_ch_nco1); | ||||||
| 	dma_channel_configure(dma_ch_mix2, &dma_conf, &PIO->txf[SM_LO], NULL, LO_PHASE_WORDS, | 	dma_channel_configure(dma_ch_mix, &dma_conf, &PIO->txf[SM_LO], lo_phase, LO_PHASE_WORDS, | ||||||
| 			      false); | 			      false); | ||||||
| 
 | 
 | ||||||
| 	/* Trigger accumulator values push. */ | 	/* Trigger accumulator values push. */ | ||||||
|  | @ -383,7 +397,7 @@ static void rf_rx_start() | ||||||
| 	init_rx(); | 	init_rx(); | ||||||
| 
 | 
 | ||||||
| 	dma_channel_start(dma_ch_rx1); | 	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); | 	dma_channel_start(dma_ch_samp_cos); | ||||||
| 
 | 
 | ||||||
| 	pio_set_sm_mask_enabled(PIO, 0x0f, true); | 	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_rx1); | ||||||
| 	dma_channel_clear_chain_to(dma_ch_rx2); | 	dma_channel_clear_chain_to(dma_ch_rx2); | ||||||
| 	dma_channel_clear_chain_to(dma_ch_mix1); | 	dma_channel_clear_chain_to(dma_ch_nco1); | ||||||
| 	dma_channel_clear_chain_to(dma_ch_mix2); | 	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_clear_chain_to(dma_ch_samp_cos); | ||||||
| 
 | 
 | ||||||
| 	dma_channel_abort(dma_ch_rx1); | 	dma_channel_abort(dma_ch_rx1); | ||||||
| 	dma_channel_abort(dma_ch_rx2); | 	dma_channel_abort(dma_ch_rx2); | ||||||
| 	dma_channel_abort(dma_ch_mix1); | 	dma_channel_abort(dma_ch_nco1); | ||||||
| 	dma_channel_abort(dma_ch_mix2); | 	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_abort(dma_ch_samp_cos); | ||||||
| 
 | 
 | ||||||
| 	dma_channel_cleanup(dma_ch_rx1); | 	dma_channel_cleanup(dma_ch_rx1); | ||||||
| 	dma_channel_cleanup(dma_ch_rx2); | 	dma_channel_cleanup(dma_ch_rx2); | ||||||
| 	dma_channel_cleanup(dma_ch_mix1); | 	dma_channel_cleanup(dma_ch_nco1); | ||||||
| 	dma_channel_cleanup(dma_ch_mix2); | 	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_cleanup(dma_ch_samp_cos); | ||||||
| 
 | 
 | ||||||
| 	dma_channel_unclaim(dma_ch_rx1); | 	dma_channel_unclaim(dma_ch_rx1); | ||||||
| 	dma_channel_unclaim(dma_ch_rx2); | 	dma_channel_unclaim(dma_ch_rx2); | ||||||
| 	dma_channel_unclaim(dma_ch_mix1); | 	dma_channel_unclaim(dma_ch_nco1); | ||||||
| 	dma_channel_unclaim(dma_ch_mix2); | 	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_channel_unclaim(dma_ch_samp_cos); | ||||||
| 
 | 
 | ||||||
| 	dma_ch_rx1 = -1; | 	dma_ch_rx1 = -1; | ||||||
| 	dma_ch_rx2 = -1; | 	dma_ch_rx2 = -1; | ||||||
| 	dma_ch_mix1 = -1; | 	dma_ch_nco1 = -1; | ||||||
| 	dma_ch_mix2 = -1; | 	dma_ch_nco2 = -1; | ||||||
|  | 	dma_ch_nco3 = -1; | ||||||
|  | 	dma_ch_mix = -1; | ||||||
| 	dma_ch_samp_cos = -1; | 	dma_ch_samp_cos = -1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -572,13 +596,13 @@ static void run_command(uint8_t cmd, uint32_t arg) | ||||||
| 	if (0x01 == cmd) { | 	if (0x01 == cmd) { | ||||||
| 		/* Tune to a new center frequency */ | 		/* Tune to a new center frequency */ | ||||||
| 		frequency = arg; | 		frequency = arg; | ||||||
| 		rx_lo_init(frequency + sample_rate, true); | 		rx_lo_init(frequency + sample_rate); | ||||||
| 	} else if (0x02 == cmd) { | 	} else if (0x02 == cmd) { | ||||||
| 		/* Set the rate at which IQ sample pairs are sent */ | 		/* Set the rate at which IQ sample pairs are sent */ | ||||||
| 		sample_rate = arg; | 		sample_rate = arg; | ||||||
| 		dc_level = CLK_SYS_HZ / sample_rate / 2; | 		dc_level = CLK_SYS_HZ / sample_rate / 2; | ||||||
| 		dma_timer_set_fraction(dma_t_samp, 1, CLK_SYS_HZ / (sample_rate * DECIMATE)); | 		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) { | 	} else if (0x04 == cmd) { | ||||||
| 		/* Set the tuner gain level */ | 		/* Set the tuner gain level */ | ||||||
| 		gain = INIT_GAIN * powf(10.0f, arg / 200.0f); | 		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); | 	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_t_samp = dma_claim_unused_timer(true); | ||||||
| 	dma_timer_set_fraction(dma_t_samp, 1, CLK_SYS_HZ / (sample_rate * DECIMATE)); | 	dma_timer_set_fraction(dma_t_samp, 1, CLK_SYS_HZ / (sample_rate * DECIMATE)); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue