/* PiTxFt - Raspberry Pi ft transmitter Copyright (C) 2021 Miegl See https://github.com/Miegl/PiTxFt */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mailbox.h" #define MBFILE DEVICE_FILE_NAME // From mailbox.h #if (RASPI) == 1 // Original Raspberry Pi 1 #define PERIPH_VIRT_BASE 0x20000000 #define PERIPH_PHYS_BASE 0x7e000000 #define DRAM_PHYS_BASE 0x40000000 #define MEM_FLAG 0x0c #define CLOCK_BASE 19.2e6 #define DMA_CHANNEL 14 #elif (RASPI) == 2 // Raspberry Pi 2 & 3 #define PERIPH_VIRT_BASE 0x3f000000 #define PERIPH_PHYS_BASE 0x7e000000 #define DRAM_PHYS_BASE 0xc0000000 #define MEM_FLAG 0x04 #define CLOCK_BASE 19.2e6 #define DMA_CHANNEL 14 #elif (RASPI) == 4 // Raspberry Pi 4 #define PERIPH_VIRT_BASE 0xfe000000 #define PERIPH_PHYS_BASE 0x7e000000 #define DRAM_PHYS_BASE 0xc0000000 #define MEM_FLAG 0x04 #define CLOCK_BASE 54.0e6 #define DMA_CHANNEL 6 #else #error Unknown Raspberry Pi version (variable RASPI) #endif #define DMA_BASE_OFFSET 0x00007000 #define PWM_BASE_OFFSET 0x0020C000 #define PWM_LEN 0x28 #define CLK_BASE_OFFSET 0x00101000 #define CLK_LEN 0x1300 #define GPIO_BASE_OFFSET 0x00200000 #define GPIO_LEN 0x100 #define PCM_BASE_OFFSET 0x00203000 #define PCM_LEN 0x24 #define PAD_BASE_OFFSET 0x00100000 #define PAD_LEN (0x40/4) //0x64 #define DMA_VIRT_BASE (PERIPH_VIRT_BASE + DMA_BASE_OFFSET) #define PWM_VIRT_BASE (PERIPH_VIRT_BASE + PWM_BASE_OFFSET) #define CLK_VIRT_BASE (PERIPH_VIRT_BASE + CLK_BASE_OFFSET) #define GPIO_VIRT_BASE (PERIPH_VIRT_BASE + GPIO_BASE_OFFSET) #define PAD_VIRT_BASE (PERIPH_VIRT_BASE + PAD_BASE_OFFSET) #define PCM_VIRT_BASE (PERIPH_VIRT_BASE + PCM_BASE_OFFSET) #define PWM_PHYS_BASE (PERIPH_PHYS_BASE + PWM_BASE_OFFSET) #define PCM_PHYS_BASE (PERIPH_PHYS_BASE + PCM_BASE_OFFSET) #define GPIO_PHYS_BASE (PERIPH_PHYS_BASE + GPIO_BASE_OFFSET) // GPIO #define GPFSEL0 (0x00/4) #define GPFSEL1 (0x04/4) #define GPFSEL2 (0x08/4) #define GPPUD (0x94/4) #define GPPUDCLK0 (0x98/4) #define GPPUDCLK1 (0x9C/4) #define CORECLK_CNTL (0x08/4) #define CORECLK_DIV (0x0c/4) #define GPCLK_CNTL (0x70/4) #define GPCLK_DIV (0x74/4) #define EMMCCLK_CNTL (0x1C0/4) #define EMMCCLK_DIV (0x1C4/4) #define CM_LOCK (0x114/4) #define CM_LOCK_FLOCKA (1<<8) #define CM_LOCK_FLOCKB (1<<9) #define CM_LOCK_FLOCKC (1<<10) #define CM_LOCK_FLOCKD (1<<11) #define CM_LOCK_FLOCKH (1<<12) #define CM_PLLA (0x104/4) #define CM_PLLC (0x108/4) #define CM_PLLD (0x10c/4) #define CM_PLLH (0x110/4) #define CM_PLLB (0x170/4) #define A2W_PLLA_ANA0 (0x1010/4) #define A2W_PLLC_ANA0 (0x1030/4) #define A2W_PLLD_ANA0 (0x1050/4) #define A2W_PLLH_ANA0 (0x1070/4) #define A2W_PLLB_ANA0 (0x10f0/4) #define A2W_PLL_KA_SHIFT 7 #define A2W_PLL_KI_SHIFT 19 #define A2W_PLL_KP_SHIFT 15 #define PLLA_CTRL (0x1100/4) #define PLLA_FRAC (0x1200/4) #define PLLA_DSI0 (0x1300/4) #define PLLA_CORE (0x1400/4) #define PLLA_PER (0x1500/4) #define PLLA_CCP2 (0x1600/4) #define PLLB_CTRL (0x11e0/4) #define PLLB_FRAC (0x12e0/4) #define PLLB_ARM (0x13e0/4) #define PLLB_SP0 (0x14e0/4) #define PLLB_SP1 (0x15e0/4) #define PLLB_SP2 (0x16e0/4) #define PLLC_CTRL (0x1120/4) #define PLLC_FRAC (0x1220/4) #define PLLC_CORE2 (0x1320/4) #define PLLC_CORE1 (0x1420/4) #define PLLC_PER (0x1520/4) #define PLLC_CORE0 (0x1620/4) #define PLLD_CTRL (0x1140/4) #define PLLD_FRAC (0x1240/4) #define PLLD_DSI0 (0x1340/4) #define PLLD_CORE (0x1440/4) #define PLLD_PER (0x1540/4) #define PLLD_DSI1 (0x1640/4) #define PLLH_CTRL (0x1160/4) #define PLLH_FRAC (0x1260/4) #define PLLH_AUX (0x1360/4) #define PLLH_RCAL (0x1460/4) #define PLLH_PIX (0x1560/4) #define PLLH_STS (0x1660/4) // PWM #define PWM_CTL (0x00/4) #define PWM_DMAC (0x08/4) #define PWM_RNG1 (0x10/4) #define PWM_RNG2 (0x20/4) #define PWM_FIFO (0x18/4) #define PWMCLK_CNTL 40 #define PWMCLK_DIV 41 #define PWMCTL_PWEN1 (1<<0) #define PWMCTL_MODE1 (1<<1) #define PWMCTL_RPTL1 (1<<2) #define PWMCTL_POLA1 (1<<4) #define PWMCTL_USEF1 (1<<5) #define PWMCTL_CLRF (1<<6) #define PWMCTL_MSEN1 (1<<7) #define PWMCTL_PWEN2 (1<<8) #define PWMCTL_MODE2 (1<<9) #define PWMCTL_RPTL2 (1<<10) #define PWMCTL_USEF2 (1<<13) #define PWMCTL_MSEN2 (1<<15) #define PWMDMAC_ENAB (1<<31) #define PWMDMAC_THRSHLD ((15<<8)|(15<<0)) // PCM #define PCM_CS_A (0x00/4) #define PCM_FIFO_A (0x04/4) #define PCM_MODE_A (0x08/4) #define PCM_RXC_A (0x0c/4) #define PCM_TXC_A (0x10/4) #define PCM_DREQ_A (0x14/4) #define PCM_INTEN_A (0x18/4) #define PCM_INT_STC_A (0x1c/4) #define PCM_GRAY (0x20/4) #define PCMCLK_CNTL 38 #define PCMCLK_DIV 39 // PAD #define GPIO_PAD_0_27 (0x2C/4) #define GPIO_PAD_28_45 (0x30/4) #define GPIO_PAD_46_52 (0x34/4) // DMA #define DMA_CHANNEL_MAX 14 #define DMA_CHANNEL_SIZE 0x100 #define BCM2708_DMA_ACTIVE (1<<0) #define BCM2708_DMA_END (1<<1) #define BCM2708_DMA_INT (1<<2) #define BCM2708_DMA_WAIT_RESP (1<<3) #define BCM2708_DMA_D_DREQ (1<<6) #define BCM2708_DMA_DST_IGNOR (1<<7) #define BCM2708_DMA_SRC_INC (1<<8) #define BCM2708_DMA_SRC_IGNOR (1<<11) #define BCM2708_DMA_NO_WIDE_BURSTS (1<<26) #define BCM2708_DMA_DISDEBUG (1<<28) #define BCM2708_DMA_ABORT (1<<30) #define BCM2708_DMA_RESET (1<<31) #define BCM2708_DMA_PER_MAP(x) ((x)<<16) #define BCM2708_DMA_PRIORITY(x) ((x)&0xf << 16) #define BCM2708_DMA_PANIC_PRIORITY(x) ((x)&0xf << 20) #define DMA_CS (0x00/4) #define DMA_CONBLK_AD (0x04/4) #define DMA_DEBUG (0x20/4) #define DMA_CS_RESET (1<<31) #define DMA_CS_ABORT (1<<30) #define DMA_CS_DISDEBUG (1<<29) #define DMA_CS_WAIT_FOR_OUTSTANDING_WRITES (1<<28) #define DMA_CS_INT (1<<2) #define DMA_CS_END (1<<1) #define DMA_CS_ACTIVE (1<<0) #define DMA_CS_PRIORITY(x) ((x)&0xf << 16) #define DMA_CS_PANIC_PRIORITY(x) ((x)&0xf << 20) #define DREQ_PCM_TX 2 #define DREQ_PCM_RX 3 #define DREQ_SMI 4 #define DREQ_PWM 5 #define DREQ_SPI_TX 6 #define DREQ_SPI_RX 7 #define DREQ_SPI_SLAVE_TX 8 #define DREQ_SPI_SLAVE_RX 9 #define MEM_FLAG_DISCARDABLE (1 << 0) /* can be resized to 0 at any time. Use for cached data */ #define MEM_FLAG_NORMAL (0 << 2) /* normal allocating alias. Don't use from ARM */ #define MEM_FLAG_DIRECT (1 << 2) /* 0xC alias uncached */ #define MEM_FLAG_COHERENT (2 << 2) /* 0x8 alias. Non-allocating in L2 but coherent */ #define MEM_FLAG_L1_NONALLOCATING (MEM_FLAG_DIRECT | MEM_FLAG_COHERENT) /* Allocating in L2 */ #define MEM_FLAG_ZERO (1 << 4) /* initialise buffer to all zeros */ #define MEM_FLAG_NO_INIT (1 << 5) /* don't initialise (default is initialise to all ones */ #define MEM_FLAG_HINT_PERMALOCK (1 << 6) /* Likely to be locked for long periods of time. */ #define BUS_TO_PHYS(x) ((x)&~0xC0000000) #define PAGE_SIZE 4096 #define PAGE_SHIFT 12 #define NUM_PAGES ((sizeof(struct control_data_s) + PAGE_SIZE - 1) >> PAGE_SHIFT) #define NUM_SAMPLES 64000 #define NUM_CBS (NUM_SAMPLES * 2) struct __attribute__((__packed__)) ft { double frequency; uint32_t duration; uint32_t padding; }; typedef struct { uint32_t info, src, dst, length, stride, next, pad[2]; } dma_cb_t; static struct { int handle; /* From mbox_open() */ unsigned mem_ref; /* From mem_alloc() */ unsigned bus_addr; /* From mem_lock() */ uint8_t *virt_addr; /* From mapmem() */ } mbox; struct control_data_s { dma_cb_t cb[NUM_CBS]; uint32_t sample[NUM_SAMPLES]; }; static struct control_data_s *ctl; static volatile uint32_t *pwm_reg; static volatile uint32_t *clk_reg; static volatile uint32_t *dma_reg; static volatile uint32_t *gpio_reg; static volatile uint32_t *pcm_reg; static volatile uint32_t *pad_reg; static void udelay(int us) { struct timespec ts = { 0, us * 1000 }; nanosleep(&ts, NULL); } static void terminate(int num) { // Stop outputting and generating the clock. if (clk_reg && gpio_reg && mbox.virt_addr) { // Set GPIOs to be an output (instead of ALT FUNC 0, which is the clock). gpio_reg[0] = (gpio_reg[0] & ~(7 << 12)) | (1 << 12); //GPIO4 udelay(10); gpio_reg[2] = (gpio_reg[2] & ~(7 << 0)) | (1 << 0); //GPIO20 udelay(10); gpio_reg[3] = (gpio_reg[3] & ~(7 << 6)) | (1 << 6); //GPIO32 udelay(10); //gpio_reg[3] = (gpio_reg[3] & ~(7 << 12)) | (1 << 12); //GPIO34 - Doesn't work on Pi 3, 3B+, Zero W //udelay(10); // Disable the clock generator. clk_reg[GPCLK_CNTL] = 0x5A; } if (dma_reg && mbox.virt_addr) { dma_reg[DMA_CS] = BCM2708_DMA_RESET; udelay(10); } if (mbox.virt_addr != NULL) { unmapmem(mbox.virt_addr, NUM_PAGES * PAGE_SIZE); mem_unlock(mbox.handle, mbox.mem_ref); mem_free(mbox.handle, mbox.mem_ref); } printf("Terminating: cleanly deactivated the DMA engine and killed the carrier.\n"); exit(num); } static void fatal(char *fmt, ...) { va_list ap; fprintf(stderr,"ERROR: "); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); terminate(0); } static void warn(char *fmt, ...) { va_list ap; fprintf(stderr,"WARNING: "); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } static uint32_t mem_virt_to_phys(void *virt) { uint32_t offset = (uint8_t *)virt - mbox.virt_addr; return mbox.bus_addr + offset; } static uint32_t mem_phys_to_virt(uint32_t phys) { return phys - (uint32_t)mbox.bus_addr + (uint32_t)mbox.virt_addr; } static void *map_peripheral(uint32_t base, uint32_t len) { int fd = open("/dev/mem", O_RDWR | O_SYNC); void * vaddr; if (fd < 0) fatal("Failed to open /dev/mem: %m.\n"); vaddr = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, base); if (vaddr == MAP_FAILED) fatal("Failed to map peripheral at 0x%08x: %m.\n", base); close(fd); return vaddr; } int tx(uint32_t carrier_freq, int divider, char *ft_file, float ppm, int power, int gpio, bool repeat) { // Catch only important signals for (int i = 0; i < 25; i++) { struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = terminate; sigaction(i, &sa, NULL); } FILE *fd = NULL; if(ft_file != NULL) { if(ft_file[0] == '-') { if(!(fd = freopen(NULL, "rb", stdin))) { fatal("Error: could not reopen stdin.\n"); } else { printf("Using stdin for input.\n"); } } else { if(!(fd = fopen(ft_file, "rb"))) { fatal("Error: could not open input file %s.\n", ft_file); } else { printf("Using input file: %s\n", ft_file); } } } else { fatal("Error: no input file specified.\n"); } dma_reg = map_peripheral(DMA_VIRT_BASE, (DMA_CHANNEL_SIZE * (DMA_CHANNEL_MAX + 1))); dma_reg = dma_reg + ((DMA_CHANNEL_SIZE / sizeof(int)) * (DMA_CHANNEL)); pwm_reg = map_peripheral(PWM_VIRT_BASE, PWM_LEN); clk_reg = map_peripheral(CLK_VIRT_BASE, CLK_LEN); gpio_reg = map_peripheral(GPIO_VIRT_BASE, GPIO_LEN); pcm_reg = map_peripheral(PCM_VIRT_BASE, PCM_LEN); pad_reg = map_peripheral(PAD_VIRT_BASE, PAD_LEN); uint32_t freq_ctl; // Use the mailbox interface to the VC to ask for physical memory. mbox.handle = mbox_open(); if (mbox.handle < 0) fatal("Failed to open mailbox. Check kernel support for vcio / BCM2708 mailbox.\n"); printf("Allocating physical memory: size = %d, ", NUM_PAGES * PAGE_SIZE); if(!(mbox.mem_ref = mem_alloc(mbox.handle, NUM_PAGES * PAGE_SIZE, PAGE_SIZE, MEM_FLAG))) { fatal("Could not allocate memory.\n"); } printf("mem_ref = %u, ", mbox.mem_ref); if(!(mbox.bus_addr = mem_lock(mbox.handle, mbox.mem_ref))) { fatal("Could not lock memory.\n"); } printf("bus_addr = %x, ", mbox.bus_addr); if(!(mbox.virt_addr = mapmem(BUS_TO_PHYS(mbox.bus_addr), NUM_PAGES * PAGE_SIZE))) { fatal("Could not map memory.\n"); } printf("virt_addr = %p\n", mbox.virt_addr); clk_reg[GPCLK_CNTL] = (0x5a<<24) | (1<<4) | (4); udelay(100); clk_reg[CM_PLLA] = 0x5A00022A; // Enable PLLA_PER udelay(100); int ana[4]; for (int i = 3; i >= 0; i--) { ana[i] = clk_reg[(A2W_PLLA_ANA0) + i]; } ana[1]&=~(1<<14); for (int i = 3; i >= 0; i--) { clk_reg[(A2W_PLLA_ANA0) + i] = (0x5A << 24) | ana[i]; } udelay(100); clk_reg[PLLA_CORE] = (0x5a<<24) | (1<<8); // Disable clk_reg[PLLA_PER] = 0x5A000001; // Div udelay(100); // Adjust PLLA frequency freq_ctl = (unsigned int)(((carrier_freq*divider)/CLOCK_BASE*((double)(1<<20)))); clk_reg[PLLA_CTRL] = (0x5a<<24) | (0x21<<12) | (freq_ctl>>20); // Integer part freq_ctl&=0xFFFFF; clk_reg[PLLA_FRAC] = (0x5a<<24) | (freq_ctl&0xFFFFC); // Fractional part udelay(100); if ((clk_reg[CM_LOCK] & CM_LOCK_FLOCKA) > 0) printf("Master PLLA Locked\n"); else printf("Warning: Master PLLA NOT Locked\n"); // Program GPCLK integer division //int clktmp; //clktmp = clk_reg[GPCLK_CNTL]; //clk_reg[GPCLK_CNTL] = (0xF0F&clktmp) | (0x5a<<24); // Clear run //udelay(100); clk_reg[GPCLK_DIV] = (0x5a<<24) | (divider<<12); udelay(100); clk_reg[GPCLK_CNTL] = (0x5a<<24) | (4); // Source = PLLA (4) udelay(100); clk_reg[GPCLK_CNTL] = (0x5a<<24) | (1<<4) | (4); // Run, Source = PLLA (4) udelay(100); // Drive Strength: 0 = 2mA, 7 = 16mA. Ref: https://www.scribd.com/doc/101830961/GPIO-Pads-Control2 pad_reg[GPIO_PAD_0_27] = 0x5a000018 + power; pad_reg[GPIO_PAD_28_45] = 0x5a000018 + power; udelay(100); int reg = gpio / 10; int shift = (gpio % 10) * 3; int mode; if(gpio == 20) { mode = 2; } else { mode = 4; } // GPIO needs to be ALT FUNC 0 to output the clock gpio_reg[reg] = (gpio_reg[reg] & ~(7 << shift)) | (mode << shift); udelay(100); ctl = (struct control_data_s *) mbox.virt_addr; dma_cb_t *cbp = ctl->cb; for (int i = 0; i < NUM_SAMPLES; i++) { ctl->sample[i] = 0x5a << 24 | freq_ctl; // Silence // Write a frequency sample cbp->info = BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP; cbp->src = mem_virt_to_phys(ctl->sample + i); cbp->dst = PERIPH_PHYS_BASE + (PLLA_FRAC<<2) + CLK_BASE_OFFSET; cbp->length = 4; cbp->stride = 0; cbp->next = mem_virt_to_phys(cbp + 1); cbp++; // Delay cbp->info = BCM2708_DMA_NO_WIDE_BURSTS | BCM2708_DMA_WAIT_RESP | BCM2708_DMA_D_DREQ | BCM2708_DMA_PER_MAP(5); cbp->src = mem_virt_to_phys(mbox.virt_addr); cbp->dst = PERIPH_PHYS_BASE + (PWM_FIFO<<2) + PWM_BASE_OFFSET; cbp->length = 4; cbp->stride = 0; cbp->next = mem_virt_to_phys(cbp + 1); cbp++; } cbp--; cbp->next = mem_virt_to_phys(mbox.virt_addr); // Here we define the rate at which we want to update the GPCLK control register double srdivider = (((double)carrier_freq*divider/1e3)/(2*200*(1.+ppm/1.e6))); uint32_t idivider = (uint32_t)srdivider; uint32_t fdivider = (uint32_t)((srdivider - idivider)*pow(2, 12)); printf("PPM correction is %.4f, divider is %.4f (%d + %d*2^-12).\n", ppm, srdivider, idivider, fdivider); pwm_reg[PWM_CTL] = 0; udelay(100); clk_reg[PWMCLK_CNTL] = (0x5a<<24) | (4); // Source = PLLA & disable udelay(100); clk_reg[PWMCLK_DIV] = (0x5a<<24) | (idivider<<12) | fdivider; udelay(100); clk_reg[PWMCLK_CNTL] = (0x5a<<24) | (1<<9) | (1<<4) | (4); // Source = PLLA, enable, MASH setting 1 udelay(100); pwm_reg[PWM_RNG1] = 2; udelay(100); pwm_reg[PWM_DMAC] = PWMDMAC_ENAB | PWMDMAC_THRSHLD; udelay(100); pwm_reg[PWM_CTL] = PWMCTL_CLRF; udelay(100); pwm_reg[PWM_CTL] = PWMCTL_USEF1 | PWMCTL_MODE1 | PWMCTL_PWEN1 | PWMCTL_MSEN1; //pwm_reg[PWM_CTL] = PWMCTL_USEF1 | PWMCTL_PWEN1; udelay(100); // Initialise the DMA dma_reg[DMA_CS] = BCM2708_DMA_RESET; udelay(100); dma_reg[DMA_CS] = BCM2708_DMA_INT | BCM2708_DMA_END; dma_reg[DMA_CONBLK_AD] = mem_virt_to_phys(ctl->cb); dma_reg[DMA_DEBUG] = 7; // clear debug error flags dma_reg[DMA_CS] = BCM2708_DMA_PRIORITY(15) | BCM2708_DMA_PANIC_PRIORITY(15) | BCM2708_DMA_DISDEBUG | BCM2708_DMA_ACTIVE; uint32_t last_cb = (uint32_t)ctl->cb; struct ft* ft_data = NULL; size_t ft_len = 500; size_t ft_index = 0; ft_data = calloc(ft_len, sizeof(struct ft)); //while(ft_index += fread(&ft_data[ft_index], sizeof(struct ft), ft_len - ft_index, fd) == (ft_len - ft_index)) { // ft_len *= 2; // ft_data = reallocarray(ft_data, ft_len, sizeof(struct ft)); //} while(fread(&ft_data[ft_index++], sizeof(struct ft), 1, fd) > 0) { if(ft_index == ft_len - 1) { ft_len *= 2; ft_data = reallocarray(ft_data, ft_len, sizeof(struct ft)); } } double *buffer; size_t buffer_len = 0; size_t buffer_index = 0; for(size_t i = 0; i < ft_index; i++) { buffer_len += (uint64_t)ft_data[i].duration*200000/1000000000; } buffer = calloc(buffer_len, sizeof(double)); for(size_t i = 0; i < ft_index; i++) { for(size_t j = 0; j < ((uint64_t)ft_data[i].duration*200000/1000000000); j++) { buffer[buffer_index++] = ft_data[i].frequency; } } buffer_index = 0; printf("Starting to transmit on %3.1f MHz.\n", carrier_freq/1e6); for (;;) { uint32_t cur_cb = (int)mem_phys_to_virt(dma_reg[DMA_CONBLK_AD]); int last_sample = (last_cb - (int)mbox.virt_addr) / (sizeof(dma_cb_t) * 2); int this_sample = (cur_cb - (int)mbox.virt_addr) / (sizeof(dma_cb_t) * 2); int free_slots = this_sample - last_sample; if (free_slots < 0) free_slots += NUM_SAMPLES; while (free_slots >= 1) { // Get more baseband samples if necessary if(repeat && (buffer_index == buffer_len)) buffer_index = 0; uint32_t freqval = (uint32_t)(((buffer[buffer_index++]*divider)/CLOCK_BASE*((double)(1<<20)))); freqval &= 0xFFFFF; ctl->sample[last_sample++] = (0x5A << 24 | freqval); if (last_sample == NUM_SAMPLES) last_sample = 0; free_slots -= 1; } last_cb = (uint32_t)mbox.virt_addr + last_sample * sizeof(dma_cb_t) * 2; usleep(5000); } return 0; } int main(int argc, char **argv) { int opt; char *ft_file = NULL; uint32_t carrier_freq = 87600000; float ppm = 0; int divc = 0; int power = 7; int gpio = 4; bool repeat = false; const char *short_opt = "i:f:p:d:w:g:rh"; struct option long_opt[] = { {"input", required_argument, NULL, 'i'}, {"freq", required_argument, NULL, 'f'}, {"ppm", required_argument, NULL, 'p'}, {"div", required_argument, NULL, 'd'}, {"power", required_argument, NULL, 'w'}, {"gpio", required_argument, NULL, 'g'}, {"repeat", no_argument, NULL, 'r'}, {"help", no_argument, NULL, 'h'}, { 0, 0, 0, 0 } }; while((opt = getopt_long(argc, argv, short_opt, long_opt, NULL)) != -1) { switch(opt) { case -1: case 0: break; case 'i': //input file ft_file = optarg; break; case 'f': //freq carrier_freq = 1e6 * atof(optarg); break; case 'p': //ppm ppm = atof(optarg); break; case 'd': //div divc = atoi(optarg); break; case 'w': //power power = atoi(optarg); if(power < 0 || power > 7) fatal("Output power has to be set in range of 0 - 7\n"); break; case 'g': //gpio gpio = atoi(optarg); if(gpio != 4 && gpio != 20 && gpio != 32) // && gpio != 34) fatal("Available GPIO pins: 4,20,32\n"); break; case 'r': //repeat repeat = true; break; case 'h': //help fatal("Help:\n" "Syntax: pitxft [--input (-i) file] [--freq (-f) frequency] [--ppm (-p) ppm-error] ...\n"); break; case ':': fatal("%s: option '-%c' requires an argument\n", argv[0], optopt); break; case '?': default: fatal("%s: option '-%c' is invalid. See -h (--help)\n", argv[0], optopt); break; } } double xtal_freq_recip=1.0/CLOCK_BASE; int divider, best_divider = 0; int min_int_multiplier, max_int_multiplier; int int_multiplier; double frac_multiplier; int fom, best_fom = 0; int solution_count = 0; for(divider = 2; divider < 50; divider += 1) { if(carrier_freq * divider > 1400e6) break; max_int_multiplier=((int)((double)(carrier_freq + 10 + (1 * 1000)) * divider * xtal_freq_recip)); min_int_multiplier=((int)((double)(carrier_freq - 10 - (1 * 1000)) * divider * xtal_freq_recip)); if(min_int_multiplier != max_int_multiplier) continue; solution_count++; fom = 0; if(carrier_freq * divider > 900e6) fom++; // Prefer frequencies close to 1.0 Ghz if(carrier_freq * divider < 1100e6) fom++; if(carrier_freq * divider > 800e6) fom++; if(carrier_freq * divider < 1200e6) fom++; frac_multiplier = ((double)(carrier_freq) * divider * xtal_freq_recip); int_multiplier = (int)frac_multiplier; frac_multiplier = frac_multiplier - int_multiplier; if((frac_multiplier > 0.2) && (frac_multiplier < 0.8)) fom++; // Prefer mulipliers away from integer boundaries if(fom > best_fom) // Best match so far { best_fom = fom; best_divider = divider; } } if(divc) { best_divider = divc; } else if(!solution_count & !best_divider) { fatal("No tuning solution found. You can specify the divider manually by setting the -div parameter.\n"); } printf("Carrier: %3.2f Mhz, VCO: %4.1f MHz, Multiplier: %f, Divider: %d\n", carrier_freq/1e6, (double)carrier_freq * best_divider / 1e6, carrier_freq * best_divider * xtal_freq_recip, best_divider); int errcode = tx(carrier_freq, best_divider, ft_file, ppm, power, gpio, repeat); terminate(errcode); }