Initial firmware commit.

This commit is contained in:
Jared Boone 2015-07-08 08:39:24 -07:00
parent 626e863257
commit dc6fee8370
357 changed files with 83134 additions and 0 deletions

296
firmware/application/Makefile Executable file
View File

@ -0,0 +1,296 @@
#
# Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
#
# This file is part of PortaPack.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
##############################################################################
# Build global options
# NOTE: Can be overridden externally.
#
# Compiler options here.
ifeq ($(USE_OPT),)
USE_OPT = -mthumb \
-Os -ggdb3 \
-ffunction-sections \
-fdata-sections \
-fno-builtin \
-nostartfiles \
--specs=nano.specs
#-fomit-frame-pointer
endif
# C specific options here (added to USE_OPT).
ifeq ($(USE_COPT),)
USE_COPT = -std=gnu99
endif
# C++ specific options here (added to USE_OPT).
ifeq ($(USE_CPPOPT),)
USE_CPPOPT = -std=c++11 -fno-rtti -fno-exceptions
endif
# Enable this if you want the linker to remove unused code and data
ifeq ($(USE_LINK_GC),)
USE_LINK_GC = yes
endif
# Linker extra options here.
ifeq ($(USE_LDOPT),)
USE_LDOPT =
endif
# Enable this if you want link time optimizations (LTO)
ifeq ($(USE_LTO),)
USE_LTO = no
endif
# If enabled, this option allows to compile the application in THUMB mode.
ifeq ($(USE_THUMB),)
USE_THUMB = yes
endif
# Enable this if you want to see the full log while compiling.
ifeq ($(USE_VERBOSE_COMPILE),)
USE_VERBOSE_COMPILE = no
endif
#
# Build global options
##############################################################################
##############################################################################
# Architecture or project specific options
#
#
# Architecture or project specific options
##############################################################################
##############################################################################
# Project, sources and paths
#
# Define project name here
PROJECT = application
# Imported source files and paths
CHIBIOS = ../chibios
CHIBIOS_PORTAPACK = ../chibios-portapack
include $(CHIBIOS_PORTAPACK)/boards/GSG_HACKRF_ONE/board.mk
include $(CHIBIOS_PORTAPACK)/os/hal/platforms/LPC43xx_M0/platform.mk
include $(CHIBIOS)/os/hal/hal.mk
include $(CHIBIOS_PORTAPACK)/os/ports/GCC/ARMCMx/LPC43xx_M0/port.mk
include $(CHIBIOS)/os/kernel/kernel.mk
include $(CHIBIOS_PORTAPACK)/os/various/fatfs_bindings/fatfs.mk
include $(CHIBIOS)/test/test.mk
# Define linker script file here
LDSCRIPT= $(PORTLD)/LPC43xx_M0.ld
# C sources that can be compiled in ARM or THUMB mode depending on the global
# setting.
CSRC = $(PORTSRC) \
$(KERNSRC) \
$(TESTSRC) \
$(HALSRC) \
$(PLATFORMSRC) \
$(BOARDSRC) \
$(FATFSSRC)
# C++ sources that can be compiled in ARM or THUMB mode depending on the global
# setting.
CPPSRC = main.cpp \
irq_ipc.cpp \
irq_lcd_frame.cpp \
irq_controls.cpp \
event.cpp \
message_queue.cpp \
hackrf_hal.cpp \
portapack_shared_memory.cpp \
portapack_io.cpp \
i2c_pp.cpp \
spi_pp.cpp \
clock_manager.cpp \
si5351.cpp \
wm8731.cpp \
radio.cpp \
baseband_cpld.cpp \
baseband_sgpio.cpp \
tuning.cpp \
rf_path.cpp \
rffc507x.cpp \
rffc507x_spi.cpp \
max2837.cpp \
max5864.cpp \
debounce.cpp \
touch.cpp \
touch_adc.cpp \
encoder.cpp \
lcd_ili9341.cpp \
ui.cpp \
ui_text.cpp \
ui_widget.cpp \
ui_painter.cpp \
ui_focus.cpp \
ui_navigation.cpp \
ui_menu.cpp \
ui_rssi.cpp \
ui_channel.cpp \
ui_audio.cpp \
ui_font_fixed_8x16.cpp \
ui_setup.cpp \
ui_debug.cpp \
ui_console.cpp \
ui_receiver.cpp \
ui_spectrum.cpp \
receiver_model.cpp \
spectrum_color_lut.cpp \
../common/utility.cpp \
../common/debug.cpp \
../common/gcc.cpp \
m4_startup.cpp \
cpld_max5.cpp \
jtag.cpp \
cpld_update.cpp \
portapack_cpld_data.cpp
# C sources to be compiled in ARM mode regardless of the global setting.
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
# option that results in lower performance and larger code size.
ACSRC =
# C++ sources to be compiled in ARM mode regardless of the global setting.
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
# option that results in lower performance and larger code size.
ACPPSRC =
# C sources to be compiled in THUMB mode regardless of the global setting.
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
# option that results in lower performance and larger code size.
TCSRC =
# C sources to be compiled in THUMB mode regardless of the global setting.
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
# option that results in lower performance and larger code size.
TCPPSRC =
# List ASM source files here
ASMSRC = $(PORTASM)
INCDIR = ../common $(PORTINC) $(KERNINC) $(TESTINC) \
$(HALINC) $(PLATFORMINC) $(BOARDINC) \
$(FATFSINC) \
$(CHIBIOS)/os/various
#
# Project, sources and paths
##############################################################################
##############################################################################
# Compiler settings
#
MCU = cortex-m0
#TRGT = arm-elf-
TRGT = arm-none-eabi-
CC = $(TRGT)gcc
CPPC = $(TRGT)g++
# Enable loading with g++ only if you need C++ runtime support.
# NOTE: You can use C++ even without C++ support if you are careful. C++
# runtime support makes code size explode.
#LD = $(TRGT)gcc
LD = $(TRGT)g++
CP = $(TRGT)objcopy
AS = $(TRGT)gcc -x assembler-with-cpp
OD = $(TRGT)objdump
SZ = $(TRGT)size
HEX = $(CP) -O ihex
BIN = $(CP) -O binary
# ARM-specific options here
AOPT =
# THUMB-specific options here
TOPT = -mthumb -DTHUMB
# Define C warning options here
CWARN = -Wall -Wextra -Wstrict-prototypes
# Define C++ warning options here
CPPWARN = -Wall -Wextra
#
# Compiler settings
##############################################################################
##############################################################################
# Start of default section
#
# List all default C defines here, like -D_DEBUG=1
# TODO: Switch -DCRT0_INIT_DATA depending on load from RAM or SPIFI?
# NOTE: _RANDOM_TCC to kill a GCC 4.9.3 error with std::max argument types
DDEFS = -DLPC43XX -DLPC43XX_M0 -D__NEWLIB__ -DHACKRF_ONE \
-DTOOLCHAIN_GCC -DTOOLCHAIN_GCC_ARM -D_RANDOM_TCC=0
# List all default ASM defines here, like -D_DEBUG=1
DADEFS =
# List all default directories to look for include files here
DINCDIR =
# List the default directory to look for the libraries here
DLIBDIR =
# List all default libraries here
DLIBS =
#
# End of default section
##############################################################################
##############################################################################
# Start of user section
#
# List all user C define here, like -D_DEBUG=1
UDEFS =
# Define ASM defines here
UADEFS =
# List all user directories here
UINCDIR =
# List the user directory to look for the libraries here
ULIBDIR =
# List all user libraries here
ULIBS =
#
# End of user defines
##############################################################################
RULESPATH = $(CHIBIOS)/os/ports/GCC/ARMCMx
include $(RULESPATH)/rules.mk

View File

@ -0,0 +1,51 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "baseband_cpld.hpp"
#include "hackrf_gpio.hpp"
using namespace hackrf::one;
namespace baseband {
void CPLD::init() {
set_decimation_by(1);
gpios_baseband_decimation[0].output();
gpios_baseband_decimation[1].output();
gpios_baseband_decimation[2].output();
set_q_invert(false);
gpio_baseband_q_invert.output();
}
void CPLD::set_decimation_by(const uint8_t n) {
const uint8_t skip_n = n - 1;
const uint8_t value = skip_n ^ 7;
gpios_baseband_decimation[0].write(value & 1);
gpios_baseband_decimation[1].write(value & 2);
gpios_baseband_decimation[2].write(value & 4);
}
void CPLD::set_q_invert(const bool invert) {
gpio_baseband_q_invert.write(invert);
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __BASEBAND_CPLD_H__
#define __BASEBAND_CPLD_H__
#include <cstdint>
namespace baseband {
class CPLD {
public:
void init();
void set_decimation_by(const uint8_t n);
void set_q_invert(const bool invert);
private:
};
}
#endif/*__BASEBAND_CPLD_H__*/

View File

@ -0,0 +1,373 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "baseband_sgpio.hpp"
#include "baseband.hpp"
#include "utility.hpp"
namespace baseband {
/*
struct PinConfig {
P_OUT_CFG p_out_cfg;
P_OE_CFG p_oe_cfg { P_OE_CFG::GPIO_OE };
constexpr SGPIOPinConfig(
P_OUT_CFG p_out_cfg
) :
p_out_cfg(p_out_cfg)
{
}
};
static constexpr bool slice_mode_multislice = false;
static constexpr P_OUT_CFG output_multiplexing_mode =
slice_mode_multislice ? P_OUT_CFG::DOUT_DOUTM8C : P_OUT_CFG::DOUT_DOUTM8A;
static constexpr std::array<PinConfig, 16> pin_config { {
[PIN_D0] = { output_multiplexing_mode, SLICE_A },
[PIN_D1] = { output_multiplexing_mode, SLICE_I },
[PIN_D2] = { output_multiplexing_mode, },
[PIN_D3] = { output_multiplexing_mode, },
[PIN_D4] = { output_multiplexing_mode, },
[PIN_D5] = { output_multiplexing_mode, },
[PIN_D6] = { output_multiplexing_mode, },
[PIN_D7] = { output_multiplexing_mode, },
[PIN_CLKIN] = { P_OUT_CFG::DOUT_DOUTM1, },
[PIN_CAPTURE] = { P_OUT_CFG::DOUT_DOUTM1, },
[PIN_DISABLE] = { P_OUT_CFG::GPIO_OUT, },
[PIN_DIRECTION] = { P_OUT_CFG::GPIO_OUT, },
[PIN_INVERT] = { P_OUT_CFG::GPIO_OUT, },
[PIN_DECIM0] = { P_OUT_CFG::GPIO_OUT, },
[PIN_DECIM1] = { P_OUT_CFG::DOUT_DOUTM1, },
[PIN_DECIM2] = { P_OUT_CFG::GPIO_OUT, },
} };
*/
/*
static constexpr std::array<LPC_SGPIO_OUT_MUX_CFG_Type, 16> out_mux_cfg_receive {
{ },
};
static constexpr std::array<LPC_SGPIO_OUT_MUX_CFG_Type, 16> out_mux_cfg_transmit {
{ },
};
*/
enum class P_OUT_CFG : uint8_t {
DOUT_DOUTM1 = 0x0,
DOUT_DOUTM2A = 0x1,
DOUT_DOUTM2B = 0x2,
DOUT_DOUTM2C = 0x3,
GPIO_OUT = 0x4,
DOUT_DOUTM4A = 0x5,
DOUT_DOUTM4B = 0x6,
DOUT_DOUTM4C = 0x7,
CLK_OUT = 0x8,
DOUT_DOUTM8A = 0x9,
DOUT_DOUTM8B = 0xa,
DOUT_DOUTM8C = 0xb,
};
enum class P_OE_CFG : uint8_t {
GPIO_OE = 0x0,
DOUT_OEM1 = 0x4,
DOUT_OEM2 = 0x5,
DOUT_OEM4 = 0x6,
DOUT_OEM8 = 0x7,
};
enum class CONCAT_ORDER : uint8_t {
SELF_LOOP = 0x0,
TWO_SLICES = 0x1,
FOUR_SLICES = 0x2,
EIGHT_SLICES = 0x3,
};
enum class CONCAT_ENABLE : uint8_t {
EXTERNAL_DATA_PIN = 0x0,
CONCATENATE_DATA = 0x1,
};
enum class CLK_CAPTURE_MODE : uint8_t {
RISING_CLOCK_EDGE = 0,
FALLING_CLOCK_EDGE = 1,
};
enum class PARALLEL_MODE : uint8_t {
SHIFT_1_BIT_PER_CLOCK = 0x0,
SHIFT_2_BITS_PER_CLOCK = 0x1,
SHIFT_4_BITS_PER_CLOCK = 0x2,
SHIFT_1_BYTE_PER_CLOCK = 0x3,
};
enum {
PIN_D0 = 0,
PIN_D1 = 1,
PIN_D2 = 2,
PIN_D3 = 3,
PIN_D4 = 4,
PIN_D5 = 5,
PIN_D6 = 6,
PIN_D7 = 7,
PIN_CLKIN = 8,
PIN_CAPTURE = 9,
PIN_DISABLE = 10,
PIN_DIRECTION = 11,
PIN_INVERT = 12,
PIN_DECIM0 = 13,
PIN_DECIM1 = 14,
PIN_DECIM2 = 15,
};
enum class Slice : uint8_t {
A = 0,
B = 1,
C = 2,
D = 3,
E = 4,
F = 5,
G = 6,
H = 7,
I = 8,
J = 9,
K = 10,
L = 11,
M = 12,
N = 13,
O = 14,
P = 15,
};
constexpr bool slice_mode_multislice = false;
constexpr uint8_t pos_count_multi_slice = 0x1f;
constexpr uint8_t pos_count_single_slice = 0x03;
constexpr Slice slice_order[] {
Slice::A,
Slice::I,
Slice::E,
Slice::J,
Slice::C,
Slice::K,
Slice::F,
Slice::L,
Slice::B,
Slice::M,
Slice::G,
Slice::N,
Slice::D,
Slice::O,
Slice::H,
Slice::P,
};
constexpr uint32_t gpio_outreg(const Direction direction) {
return ((direction == Direction::Transmit) ? (1U << 11) : 0U) | (1U << 10);
}
constexpr uint32_t gpio_oenreg(const Direction direction) {
return
(0U << PIN_DECIM2)
| (1U << PIN_DECIM1)
| (0U << PIN_DECIM0)
| (0U << PIN_INVERT)
| (1U << PIN_DIRECTION)
| (1U << PIN_DISABLE)
| (0U << PIN_CAPTURE)
| (0U << PIN_CLKIN)
| ((direction == Direction::Transmit) ? 0xffU : 0x00U)
;
}
constexpr uint32_t out_mux_cfg(const P_OUT_CFG out, const P_OE_CFG oe) {
return
(toUType(out) << 0)
| (toUType(oe) << 4)
;
}
constexpr uint32_t data_sgpio_mux_cfg(
const CONCAT_ENABLE concat_enable,
const CONCAT_ORDER concat_order
) {
return
(1U << 0)
| (0U << 1)
| (0U << 3)
| (3U << 5)
| (1U << 7)
| (0U << 9)
| (toUType(concat_enable) << 11)
| (toUType(concat_order) << 12)
;
}
constexpr uint32_t data_slice_mux_cfg(
const PARALLEL_MODE parallel_mode,
const CLK_CAPTURE_MODE clk_capture_mode
) {
return
(0U << 0)
| (toUType(clk_capture_mode) << 1)
| (1U << 2)
| (0U << 3)
| (0U << 4)
| (toUType(parallel_mode) << 6)
| (0U << 8)
;
}
constexpr uint32_t pos(
const uint32_t pos,
const uint32_t pos_reset
) {
return
(pos << 0)
| (pos_reset << 8)
;
}
constexpr uint32_t data_pos(
const bool multi_slice
) {
return pos(
(multi_slice ? pos_count_multi_slice : pos_count_single_slice),
(multi_slice ? pos_count_multi_slice : pos_count_single_slice)
);
}
constexpr CONCAT_ENABLE data_concat_enable(
const bool input_slice,
const bool single_slice
) {
return (input_slice || single_slice)
? CONCAT_ENABLE::EXTERNAL_DATA_PIN
: CONCAT_ENABLE::CONCATENATE_DATA
;
}
constexpr CONCAT_ORDER data_concat_order(
const bool input_slice,
const bool single_slice
) {
return (input_slice || single_slice)
? CONCAT_ORDER::SELF_LOOP
: CONCAT_ORDER::EIGHT_SLICES
;
}
constexpr CLK_CAPTURE_MODE data_clk_capture_mode(
const Direction direction
) {
return (direction == Direction::Transmit)
? CLK_CAPTURE_MODE::RISING_CLOCK_EDGE
: CLK_CAPTURE_MODE::FALLING_CLOCK_EDGE
;
}
constexpr P_OUT_CFG data_p_out_cfg(
const bool multi_slice
) {
return (multi_slice)
? P_OUT_CFG::DOUT_DOUTM8C
: P_OUT_CFG::DOUT_DOUTM8A
;
}
void SGPIO::configure(const Direction direction) {
disable_all_slice_counters();
LPC_SGPIO->GPIO_OUTREG = gpio_outreg(direction);
LPC_SGPIO->GPIO_OENREG = gpio_oenreg(direction);
LPC_SGPIO->OUT_MUX_CFG[ 8] = out_mux_cfg(P_OUT_CFG::DOUT_DOUTM1, P_OE_CFG::GPIO_OE);
LPC_SGPIO->OUT_MUX_CFG[ 9] = out_mux_cfg(P_OUT_CFG::DOUT_DOUTM1, P_OE_CFG::GPIO_OE);
LPC_SGPIO->OUT_MUX_CFG[10] = out_mux_cfg(P_OUT_CFG::GPIO_OUT, P_OE_CFG::GPIO_OE);
LPC_SGPIO->OUT_MUX_CFG[11] = out_mux_cfg(P_OUT_CFG::GPIO_OUT, P_OE_CFG::GPIO_OE);
LPC_SGPIO->OUT_MUX_CFG[12] = out_mux_cfg(P_OUT_CFG::GPIO_OUT, P_OE_CFG::GPIO_OE);
LPC_SGPIO->OUT_MUX_CFG[13] = out_mux_cfg(P_OUT_CFG::GPIO_OUT, P_OE_CFG::GPIO_OE);
LPC_SGPIO->OUT_MUX_CFG[14] = out_mux_cfg(P_OUT_CFG::DOUT_DOUTM1, P_OE_CFG::GPIO_OE);
LPC_SGPIO->OUT_MUX_CFG[15] = out_mux_cfg(P_OUT_CFG::GPIO_OUT, P_OE_CFG::GPIO_OE);
const auto data_out_mux_cfg = out_mux_cfg(data_p_out_cfg(slice_mode_multislice), P_OE_CFG::GPIO_OE);
for(size_t i=0; i<8; i++) {
LPC_SGPIO->OUT_MUX_CFG[i] = data_out_mux_cfg;
}
const auto slice_gpdma = Slice::H;
const size_t slice_count = slice_mode_multislice ? 8 : 1;
const auto clk_capture_mode = data_clk_capture_mode(direction);
const auto single_slice = !slice_mode_multislice;
uint32_t slice_enable_mask = 0;
for(size_t i=0; i<slice_count; i++) {
const auto slice = slice_order[i];
const auto slice_index = toUType(slice);
const auto input_slice = (i == 0) && (direction != Direction::Transmit);
const auto concat_order = data_concat_order(input_slice, single_slice);
const auto concat_enable = data_concat_enable(input_slice, single_slice);
LPC_SGPIO->SGPIO_MUX_CFG[slice_index] = data_sgpio_mux_cfg(
concat_enable,
concat_order
);
LPC_SGPIO->SLICE_MUX_CFG[slice_index] = data_slice_mux_cfg(
PARALLEL_MODE::SHIFT_1_BYTE_PER_CLOCK,
clk_capture_mode
);
LPC_SGPIO->PRESET[slice_index] = 0;
LPC_SGPIO->COUNT[slice_index] = 0;
LPC_SGPIO->POS[slice_index] = data_pos(slice_mode_multislice);
LPC_SGPIO->REG[slice_index] = 0;
LPC_SGPIO->REG_SS[slice_index] = 0;
slice_enable_mask |= (1U << slice_index);
}
if( !slice_mode_multislice ) {
const auto slice_index = toUType(slice_gpdma);
LPC_SGPIO->SGPIO_MUX_CFG[slice_index] = data_sgpio_mux_cfg(
CONCAT_ENABLE::CONCATENATE_DATA,
CONCAT_ORDER::SELF_LOOP
);
LPC_SGPIO->SLICE_MUX_CFG[slice_index] = data_slice_mux_cfg(
PARALLEL_MODE::SHIFT_1_BIT_PER_CLOCK,
clk_capture_mode
);
LPC_SGPIO->PRESET[slice_index] = 0;
LPC_SGPIO->COUNT[slice_index] = 0;
LPC_SGPIO->POS[slice_index] = pos(0x1f, 0x1f);
LPC_SGPIO->REG[slice_index] = 0x11111111;
LPC_SGPIO->REG_SS[slice_index] = 0x11111111;
slice_enable_mask |= (1 << slice_index);
}
set_slice_counter_enables(slice_enable_mask);
}
} /* namespace baseband */

View File

@ -0,0 +1,67 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __BASEBAND_GPIO_H__
#define __BASEBAND_GPIO_H__
#include <cstdint>
#include "baseband.hpp"
#include "hal.h"
namespace baseband {
class SGPIO {
public:
void init() {
}
void configure(const Direction direction);
void streaming_enable() {
/* TODO: Any reason not to control from general GPIO facility? */
LPC_SGPIO->GPIO_OUTREG &= ~(1U << 10);
}
void streaming_disable() {
/* TODO: Any reason not to control from general GPIO facility? */
LPC_SGPIO->GPIO_OUTREG |= (1U << 10);
}
bool streaming_is_enabled() const {
/* TODO: Any reason not to control from general GPIO facility? */
return (LPC_SGPIO->GPIO_OUTREG >> 10) & 1;
}
private:
void disable_all_slice_counters() {
set_slice_counter_enables(0);
}
void set_slice_counter_enables(const uint16_t enable_mask) {
LPC_SGPIO->CTRL_ENABLE = enable_mask;
}
};
}
#endif/*__BASEBAND_GPIO_H__*/

543
firmware/application/chconf.h Executable file
View File

@ -0,0 +1,543 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
Copyright (C) 2014 Jared Boone, ShareBrained Technology
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file templates/chconf.h
* @brief Configuration file template.
* @details A copy of this file must be placed in each project directory, it
* contains the application specific kernel settings.
*
* @addtogroup config
* @details Kernel related settings and hooks.
* @{
*/
#ifndef _CHCONF_H_
#define _CHCONF_H_
/*===========================================================================*/
/**
* @name Kernel parameters and options
* @{
*/
/*===========================================================================*/
/**
* @brief System tick frequency.
* @details Frequency of the system timer that drives the system ticks. This
* setting also defines the system tick time unit.
*/
#if !defined(CH_FREQUENCY) || defined(__DOXYGEN__)
#define CH_FREQUENCY 1000
#endif
/**
* @brief Round robin interval.
* @details This constant is the number of system ticks allowed for the
* threads before preemption occurs. Setting this value to zero
* disables the preemption for threads with equal priority and the
* round robin becomes cooperative. Note that higher priority
* threads can still preempt, the kernel is always preemptive.
*
* @note Disabling the round robin preemption makes the kernel more compact
* and generally faster.
*/
#if !defined(CH_TIME_QUANTUM) || defined(__DOXYGEN__)
#define CH_TIME_QUANTUM 0
#endif
/**
* @brief Managed RAM size.
* @details Size of the RAM area to be managed by the OS. If set to zero
* then the whole available RAM is used. The core memory is made
* available to the heap allocator and/or can be used directly through
* the simplified core memory allocator.
*
* @note In order to let the OS manage the whole RAM the linker script must
* provide the @p __heap_base__ and @p __heap_end__ symbols.
* @note Requires @p CH_USE_MEMCORE.
*/
#if !defined(CH_MEMCORE_SIZE) || defined(__DOXYGEN__)
#define CH_MEMCORE_SIZE 0
#endif
/**
* @brief Idle thread automatic spawn suppression.
* @details When this option is activated the function @p chSysInit()
* does not spawn the idle thread automatically. The application has
* then the responsibility to do one of the following:
* - Spawn a custom idle thread at priority @p IDLEPRIO.
* - Change the main() thread priority to @p IDLEPRIO then enter
* an endless loop. In this scenario the @p main() thread acts as
* the idle thread.
* .
* @note Unless an idle thread is spawned the @p main() thread must not
* enter a sleep state.
*/
#if !defined(CH_NO_IDLE_THREAD) || defined(__DOXYGEN__)
#define CH_NO_IDLE_THREAD FALSE
#endif
/** @} */
/*===========================================================================*/
/**
* @name Performance options
* @{
*/
/*===========================================================================*/
/**
* @brief OS optimization.
* @details If enabled then time efficient rather than space efficient code
* is used when two possible implementations exist.
*
* @note This is not related to the compiler optimization options.
* @note The default is @p TRUE.
*/
#if !defined(CH_OPTIMIZE_SPEED) || defined(__DOXYGEN__)
#define CH_OPTIMIZE_SPEED TRUE
#endif
/** @} */
/*===========================================================================*/
/**
* @name Subsystem options
* @{
*/
/*===========================================================================*/
/**
* @brief Threads registry APIs.
* @details If enabled then the registry APIs are included in the kernel.
*
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_REGISTRY) || defined(__DOXYGEN__)
#define CH_USE_REGISTRY TRUE
#endif
/**
* @brief Threads synchronization APIs.
* @details If enabled then the @p chThdWait() function is included in
* the kernel.
*
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_WAITEXIT) || defined(__DOXYGEN__)
#define CH_USE_WAITEXIT TRUE
#endif
/**
* @brief Semaphores APIs.
* @details If enabled then the Semaphores APIs are included in the kernel.
*
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_SEMAPHORES) || defined(__DOXYGEN__)
#define CH_USE_SEMAPHORES TRUE
#endif
/**
* @brief Semaphores queuing mode.
* @details If enabled then the threads are enqueued on semaphores by
* priority rather than in FIFO order.
*
* @note The default is @p FALSE. Enable this if you have special requirements.
* @note Requires @p CH_USE_SEMAPHORES.
*/
#if !defined(CH_USE_SEMAPHORES_PRIORITY) || defined(__DOXYGEN__)
#define CH_USE_SEMAPHORES_PRIORITY FALSE
#endif
/**
* @brief Atomic semaphore API.
* @details If enabled then the semaphores the @p chSemSignalWait() API
* is included in the kernel.
*
* @note The default is @p TRUE.
* @note Requires @p CH_USE_SEMAPHORES.
*/
#if !defined(CH_USE_SEMSW) || defined(__DOXYGEN__)
#define CH_USE_SEMSW TRUE
#endif
/**
* @brief Mutexes APIs.
* @details If enabled then the mutexes APIs are included in the kernel.
*
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_MUTEXES) || defined(__DOXYGEN__)
#define CH_USE_MUTEXES TRUE
#endif
/**
* @brief Conditional Variables APIs.
* @details If enabled then the conditional variables APIs are included
* in the kernel.
*
* @note The default is @p TRUE.
* @note Requires @p CH_USE_MUTEXES.
*/
#if !defined(CH_USE_CONDVARS) || defined(__DOXYGEN__)
#define CH_USE_CONDVARS TRUE
#endif
/**
* @brief Conditional Variables APIs with timeout.
* @details If enabled then the conditional variables APIs with timeout
* specification are included in the kernel.
*
* @note The default is @p TRUE.
* @note Requires @p CH_USE_CONDVARS.
*/
#if !defined(CH_USE_CONDVARS_TIMEOUT) || defined(__DOXYGEN__)
#define CH_USE_CONDVARS_TIMEOUT TRUE
#endif
/**
* @brief Events Flags APIs.
* @details If enabled then the event flags APIs are included in the kernel.
*
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_EVENTS) || defined(__DOXYGEN__)
#define CH_USE_EVENTS TRUE
#endif
/**
* @brief Events Flags APIs with timeout.
* @details If enabled then the events APIs with timeout specification
* are included in the kernel.
*
* @note The default is @p TRUE.
* @note Requires @p CH_USE_EVENTS.
*/
#if !defined(CH_USE_EVENTS_TIMEOUT) || defined(__DOXYGEN__)
#define CH_USE_EVENTS_TIMEOUT TRUE
#endif
/**
* @brief Synchronous Messages APIs.
* @details If enabled then the synchronous messages APIs are included
* in the kernel.
*
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_MESSAGES) || defined(__DOXYGEN__)
#define CH_USE_MESSAGES TRUE
#endif
/**
* @brief Synchronous Messages queuing mode.
* @details If enabled then messages are served by priority rather than in
* FIFO order.
*
* @note The default is @p FALSE. Enable this if you have special requirements.
* @note Requires @p CH_USE_MESSAGES.
*/
#if !defined(CH_USE_MESSAGES_PRIORITY) || defined(__DOXYGEN__)
#define CH_USE_MESSAGES_PRIORITY FALSE
#endif
/**
* @brief Mailboxes APIs.
* @details If enabled then the asynchronous messages (mailboxes) APIs are
* included in the kernel.
*
* @note The default is @p TRUE.
* @note Requires @p CH_USE_SEMAPHORES.
*/
#if !defined(CH_USE_MAILBOXES) || defined(__DOXYGEN__)
#define CH_USE_MAILBOXES TRUE
#endif
/**
* @brief I/O Queues APIs.
* @details If enabled then the I/O queues APIs are included in the kernel.
*
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_QUEUES) || defined(__DOXYGEN__)
#define CH_USE_QUEUES TRUE
#endif
/**
* @brief Core Memory Manager APIs.
* @details If enabled then the core memory manager APIs are included
* in the kernel.
*
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_MEMCORE) || defined(__DOXYGEN__)
#define CH_USE_MEMCORE TRUE
#endif
/**
* @brief Heap Allocator APIs.
* @details If enabled then the memory heap allocator APIs are included
* in the kernel.
*
* @note The default is @p TRUE.
* @note Requires @p CH_USE_MEMCORE and either @p CH_USE_MUTEXES or
* @p CH_USE_SEMAPHORES.
* @note Mutexes are recommended.
*/
#if !defined(CH_USE_HEAP) || defined(__DOXYGEN__)
#define CH_USE_HEAP TRUE
#endif
/**
* @brief C-runtime allocator.
* @details If enabled the the heap allocator APIs just wrap the C-runtime
* @p malloc() and @p free() functions.
*
* @note The default is @p FALSE.
* @note Requires @p CH_USE_HEAP.
* @note The C-runtime may or may not require @p CH_USE_MEMCORE, see the
* appropriate documentation.
*/
#if !defined(CH_USE_MALLOC_HEAP) || defined(__DOXYGEN__)
#define CH_USE_MALLOC_HEAP FALSE
#endif
/**
* @brief Memory Pools Allocator APIs.
* @details If enabled then the memory pools allocator APIs are included
* in the kernel.
*
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_MEMPOOLS) || defined(__DOXYGEN__)
#define CH_USE_MEMPOOLS TRUE
#endif
/**
* @brief Dynamic Threads APIs.
* @details If enabled then the dynamic threads creation APIs are included
* in the kernel.
*
* @note The default is @p TRUE.
* @note Requires @p CH_USE_WAITEXIT.
* @note Requires @p CH_USE_HEAP and/or @p CH_USE_MEMPOOLS.
*/
#if !defined(CH_USE_DYNAMIC) || defined(__DOXYGEN__)
#define CH_USE_DYNAMIC TRUE
#endif
/** @} */
/*===========================================================================*/
/**
* @name Debug options
* @{
*/
/*===========================================================================*/
/**
* @brief Debug option, system state check.
* @details If enabled the correct call protocol for system APIs is checked
* at runtime.
*
* @note The default is @p FALSE.
*/
#if !defined(CH_DBG_SYSTEM_STATE_CHECK) || defined(__DOXYGEN__)
#define CH_DBG_SYSTEM_STATE_CHECK TRUE
#endif
/**
* @brief Debug option, parameters checks.
* @details If enabled then the checks on the API functions input
* parameters are activated.
*
* @note The default is @p FALSE.
*/
#if !defined(CH_DBG_ENABLE_CHECKS) || defined(__DOXYGEN__)
#define CH_DBG_ENABLE_CHECKS TRUE
#endif
/**
* @brief Debug option, consistency checks.
* @details If enabled then all the assertions in the kernel code are
* activated. This includes consistency checks inside the kernel,
* runtime anomalies and port-defined checks.
*
* @note The default is @p FALSE.
*/
#if !defined(CH_DBG_ENABLE_ASSERTS) || defined(__DOXYGEN__)
#define CH_DBG_ENABLE_ASSERTS TRUE
#endif
/**
* @brief Debug option, trace buffer.
* @details If enabled then the context switch circular trace buffer is
* activated.
*
* @note The default is @p FALSE.
*/
#if !defined(CH_DBG_ENABLE_TRACE) || defined(__DOXYGEN__)
#define CH_DBG_ENABLE_TRACE FALSE
#endif
/**
* @brief Debug option, stack checks.
* @details If enabled then a runtime stack check is performed.
*
* @note The default is @p FALSE.
* @note The stack check is performed in a architecture/port dependent way.
* It may not be implemented or some ports.
* @note The default failure mode is to halt the system with the global
* @p panic_msg variable set to @p NULL.
*/
#if !defined(CH_DBG_ENABLE_STACK_CHECK) || defined(__DOXYGEN__)
#define CH_DBG_ENABLE_STACK_CHECK TRUE
#endif
/**
* @brief Debug option, stacks initialization.
* @details If enabled then the threads working area is filled with a byte
* value when a thread is created. This can be useful for the
* runtime measurement of the used stack.
*
* @note The default is @p FALSE.
*/
#if !defined(CH_DBG_FILL_THREADS) || defined(__DOXYGEN__)
#define CH_DBG_FILL_THREADS TRUE
#endif
/**
* @brief Debug option, threads profiling.
* @details If enabled then a field is added to the @p Thread structure that
* counts the system ticks occurred while executing the thread.
*
* @note The default is @p TRUE.
* @note This debug option is defaulted to TRUE because it is required by
* some test cases into the test suite.
*/
#if !defined(CH_DBG_THREADS_PROFILING) || defined(__DOXYGEN__)
#define CH_DBG_THREADS_PROFILING TRUE
#endif
/** @} */
/*===========================================================================*/
/**
* @name Kernel hooks
* @{
*/
/*===========================================================================*/
/**
* @brief Threads descriptor structure extension.
* @details User fields added to the end of the @p Thread structure.
*/
#if !defined(THREAD_EXT_FIELDS) || defined(__DOXYGEN__)
#define THREAD_EXT_FIELDS \
/* Add threads custom fields here.*/ \
uint32_t switches; \
uint32_t start_ticks; \
uint32_t total_ticks;
#endif
/**
* @brief Threads initialization hook.
* @details User initialization code added to the @p chThdInit() API.
*
* @note It is invoked from within @p chThdInit() and implicitly from all
* the threads creation APIs.
*/
#if !defined(THREAD_EXT_INIT_HOOK) || defined(__DOXYGEN__)
#define THREAD_EXT_INIT_HOOK(tp) { \
/* Add threads initialization code here.*/ \
tp->switches = 0; \
tp->start_ticks = 0; \
tp->total_ticks = 0; \
}
#endif
/**
* @brief Threads finalization hook.
* @details User finalization code added to the @p chThdExit() API.
*
* @note It is inserted into lock zone.
* @note It is also invoked when the threads simply return in order to
* terminate.
*/
#if !defined(THREAD_EXT_EXIT_HOOK) || defined(__DOXYGEN__)
#define THREAD_EXT_EXIT_HOOK(tp) { \
/* Add threads finalization code here.*/ \
}
#endif
/**
* @brief Context switch hook.
* @details This hook is invoked just before switching between threads.
*/
#if !defined(THREAD_CONTEXT_SWITCH_HOOK) || defined(__DOXYGEN__)
#define THREAD_CONTEXT_SWITCH_HOOK(ntp, otp) { \
/* System halt code here.*/ \
otp->switches++; \
ntp->start_ticks = *((volatile uint32_t*)0x400C4008); \
otp->total_ticks += (ntp->start_ticks - otp->start_ticks); \
}
#endif
/**
* @brief Idle Loop hook.
* @details This hook is continuously invoked by the idle thread loop.
*/
#if !defined(IDLE_LOOP_HOOK) || defined(__DOXYGEN__)
#define IDLE_LOOP_HOOK() { \
/* Idle loop code here.*/ \
}
#endif
/**
* @brief System tick event hook.
* @details This hook is invoked in the system tick handler immediately
* after processing the virtual timers queue.
*/
#if !defined(SYSTEM_TICK_EVENT_HOOK) || defined(__DOXYGEN__)
#define SYSTEM_TICK_EVENT_HOOK() { \
/* System tick event code here.*/ \
}
#endif
/**
* @brief System halt hook.
* @details This hook is invoked in case to a system halting error before
* the system is halted.
*/
#if !defined(SYSTEM_HALT_HOOK) || defined(__DOXYGEN__)
#define SYSTEM_HALT_HOOK() { \
/* System halt code here.*/ \
}
#endif
/** @} */
/*===========================================================================*/
/* Port-specific settings (override port settings defaulted in chcore.h). */
/*===========================================================================*/
#define CORTEX_ENABLE_WFI_IDLE TRUE
#endif /* _CHCONF_H_ */
/** @} */

View File

@ -0,0 +1,540 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "clock_manager.hpp"
#include "hackrf_hal.hpp"
using namespace hackrf::one;
#include "lpc43xx_cpp.hpp"
using namespace lpc43xx;
static void set_clock(LPC_CGU_BASE_CLK_Type& clk, const cgu::CLK_SEL clock_source) {
clk.AUTOBLOCK = 1;
clk.CLK_SEL = toUType(clock_source);
}
static constexpr uint32_t systick_count(const uint32_t clock_source_f) {
return clock_source_f / CH_FREQUENCY;
}
static constexpr uint32_t systick_load(const uint32_t clock_source_f) {
return systick_count(clock_source_f) - 1;
}
constexpr uint32_t clock_source_irc_f = 12000000;
//constexpr uint32_t clock_source_gp_clkin = 20000000;
constexpr uint32_t clock_source_pll1_step_f = 100000000;
constexpr uint32_t clock_source_pll1_f = 200000000;
constexpr auto systick_count_irc = systick_load(clock_source_irc_f);
constexpr auto systick_count_pll1 = systick_load(clock_source_pll1_f);
constexpr auto systick_count_pll1_step = systick_load(clock_source_pll1_step_f);
constexpr uint32_t si5351_vco_f = 800000000;
constexpr uint32_t i2c0_bus_f = 400000;
constexpr uint32_t i2c0_high_period_ns = 900;
constexpr I2CClockConfig i2c_clock_config_400k_slow_clock {
.clock_source_f = clock_source_irc_f,
.bus_f = i2c0_bus_f,
.high_period_ns = i2c0_high_period_ns,
};
constexpr I2CClockConfig i2c_clock_config_400k_fast_clock {
.clock_source_f = clock_source_pll1_f,
.bus_f = i2c0_bus_f,
.high_period_ns = i2c0_high_period_ns,
};
constexpr I2CConfig i2c_config_slow_clock {
.high_count = i2c_clock_config_400k_slow_clock.i2c_high_count(),
.low_count = i2c_clock_config_400k_slow_clock.i2c_low_count(),
};
constexpr I2CConfig i2c_config_fast_clock {
.high_count = i2c_clock_config_400k_fast_clock.i2c_high_count(),
.low_count = i2c_clock_config_400k_fast_clock.i2c_low_count(),
};
constexpr si5351::Inputs si5351_inputs {
.f_xtal = si5351_xtal_f,
.f_clkin = si5351_clkin_f,
.clkin_div = 1,
};
static_assert(si5351_inputs.f_xtal == si5351_xtal_f, "XTAL output frequency wrong");
static_assert(si5351_inputs.f_clkin_out() == si5351_clkin_f, "CLKIN output frequency wrong");
constexpr si5351::PLLInputSource::Type si5351_pll_input_sources {
si5351::PLLInputSource::PLLA_Source_XTAL
| si5351::PLLInputSource::PLLB_Source_CLKIN
| si5351::PLLInputSource::CLKIN_Div1
};
constexpr si5351::PLL si5351_pll_xtal_25m {
.f_in = si5351_inputs.f_xtal,
.a = 32,
.b = 0,
.c = 1,
};
constexpr auto si5351_pll_a_xtal_reg = si5351_pll_xtal_25m.reg(0);
constexpr si5351::PLL si5351_pll_clkin_10m {
.f_in = si5351_inputs.f_clkin_out(),
.a = 80,
.b = 0,
.c = 1,
};
constexpr auto si5351_pll_b_clkin_reg = si5351_pll_clkin_10m.reg(1);
static_assert(si5351_pll_xtal_25m.f_vco() == si5351_vco_f, "PLL XTAL frequency wrong");
static_assert(si5351_pll_xtal_25m.p1() == 3584, "PLL XTAL P1 wrong");
static_assert(si5351_pll_xtal_25m.p2() == 0, "PLL XTAL P2 wrong");
static_assert(si5351_pll_xtal_25m.p3() == 1, "PLL XTAL P3 wrong");
static_assert(si5351_pll_clkin_10m.f_vco() == si5351_vco_f, "PLL CLKIN frequency wrong");
static_assert(si5351_pll_clkin_10m.p1() == 9728, "PLL CLKIN P1 wrong");
static_assert(si5351_pll_clkin_10m.p2() == 0, "PLL CLKIN P2 wrong");
static_assert(si5351_pll_clkin_10m.p3() == 1, "PLL CLKIN P3 wrong");
/*
constexpr si5351::MultisynthFractional si5351_ms_18m432 {
.f_src = si5351_vco_f,
.a = 43,
.b = 29,
.c = 72,
.r_div = 1,
};
*/
/*
constexpr si5351::MultisynthFractional si5351_ms_0_20m {
.f_src = si5351_vco_f,
.a = 20,
.b = 0,
.c = 1,
.r_div = 1,
};
constexpr auto si5351_ms_0_20m_reg = si5351_ms_0_20m.reg(0);
*/
constexpr si5351::MultisynthFractional si5351_ms_0_8m {
.f_src = si5351_vco_f,
.a = 50,
.b = 0,
.c = 1,
.r_div = 1,
};
constexpr auto si5351_ms_0_8m_reg = si5351_ms_0_8m.reg(clock_generator_output_codec);
constexpr si5351::MultisynthFractional si5351_ms_group {
.f_src = si5351_vco_f,
.a = 80, /* Don't care */
.b = 0,
.c = 1,
.r_div = 0,
};
constexpr auto si5351_ms_1_group_reg = si5351_ms_group.reg(clock_generator_output_cpld);
constexpr auto si5351_ms_2_group_reg = si5351_ms_group.reg(clock_generator_output_sgpio);
constexpr si5351::MultisynthFractional si5351_ms_10m {
.f_src = si5351_vco_f,
.a = 80,
.b = 0,
.c = 1,
.r_div = 0,
};
constexpr auto si5351_ms_3_10m_reg = si5351_ms_10m.reg(3);
constexpr si5351::MultisynthFractional si5351_ms_50m {
.f_src = si5351_vco_f,
.a = 16,
.b = 0,
.c = 1,
.r_div = 0,
};
// constexpr si5351::MultisynthFractional si5351_ms_40m {
// .f_src = si5351_vco_f,
// .a = 20,
// .b = 0,
// .c = 1,
// .r_div = 0,
// };
constexpr auto si5351_ms_rffc5072 = si5351_ms_50m;
constexpr auto si5351_ms_max2837 = si5351_ms_50m;
constexpr auto si5351_ms_4_reg = si5351_ms_rffc5072.reg(clock_generator_output_first_if);
constexpr auto si5351_ms_5_reg = si5351_ms_max2837.reg(clock_generator_output_second_if);
static_assert(si5351_ms_10m.f_out() == 10000000, "MS 10MHz f_out wrong");
static_assert(si5351_ms_10m.p1() == 9728, "MS 10MHz p1 wrong");
static_assert(si5351_ms_10m.p2() == 0, "MS 10MHz p2 wrong");
static_assert(si5351_ms_10m.p3() == 1, "MS 10MHz p3 wrong");
static_assert(si5351_ms_rffc5072.f_out() == rffc5072_reference_f, "RFFC5072 reference f_out wrong");
// static_assert(si5351_ms_50m.p1() == 2048, "MS 50MHz P1 wrong");
// static_assert(si5351_ms_50m.p2() == 0, "MS 50MHz P2 wrong");
// static_assert(si5351_ms_50m.p3() == 1, "MS 50MHz P3 wrong");
static_assert(si5351_ms_max2837.f_out() == max2837_reference_f, "MAX2837 reference f_out wrong");
// static_assert(si5351_ms_50m.p1() == 2048, "MS 40MHz P1 wrong");
// static_assert(si5351_ms_50m.p2() == 0, "MS 40MHz P2 wrong");
// static_assert(si5351_ms_50m.p3() == 1, "MS 40MHz P3 wrong");
constexpr si5351::MultisynthInteger si5351_ms_int_off {
.f_src = si5351_vco_f,
.a = 255,
.r_div = 0,
};
constexpr si5351::MultisynthInteger si5351_ms_int_mcu_clkin {
.f_src = si5351_vco_f,
.a = 20,
.r_div = 0,
};
constexpr auto si5351_ms6_7_off_mcu_clkin_reg = si5351::ms6_7_reg(si5351_ms_int_off, si5351_ms_int_mcu_clkin);
static_assert(si5351_ms_int_off.f_out() == 3137254, "MS int off f_out wrong");
static_assert(si5351_ms_int_off.p1() == 255, "MS int off P1 wrong");
static_assert(si5351_ms_int_mcu_clkin.f_out() == mcu_clkin_f, "MS int MCU CLKIN f_out wrong");
// static_assert(si5351_ms_int_mcu_clkin.p1() == 20, "MS int MCU CLKIN P1 wrong");
using namespace si5351;
constexpr ClockControl::Type si5351_clock_control_ms_src_xtal = ClockControl::MS_SRC_PLLA;
constexpr ClockControl::Type si5351_clock_control_ms_src_clkin = ClockControl::MS_SRC_PLLB;
constexpr ClockControls si5351_clock_control_common {
ClockControl::CLK_IDRV_6mA | ClockControl::CLK_SRC_MS_Self | ClockControl::CLK_INV_Normal | ClockControl::MS_INT_Fractional | ClockControl::CLK_PDN_Power_Off,
ClockControl::CLK_IDRV_6mA | ClockControl::CLK_SRC_MS_Group | ClockControl::CLK_INV_Normal | ClockControl::MS_INT_Integer | ClockControl::CLK_PDN_Power_Off,
ClockControl::CLK_IDRV_6mA | ClockControl::CLK_SRC_MS_Group | ClockControl::CLK_INV_Normal | ClockControl::MS_INT_Integer | ClockControl::CLK_PDN_Power_Off,
ClockControl::CLK_IDRV_8mA | ClockControl::CLK_SRC_MS_Self | ClockControl::CLK_INV_Normal | ClockControl::MS_INT_Integer | ClockControl::CLK_PDN_Power_Off,
ClockControl::CLK_IDRV_8mA | ClockControl::CLK_SRC_MS_Self | ClockControl::CLK_INV_Normal | ClockControl::MS_INT_Integer | ClockControl::CLK_PDN_Power_Off,
ClockControl::CLK_IDRV_6mA | ClockControl::CLK_SRC_MS_Self | ClockControl::CLK_INV_Normal | ClockControl::MS_INT_Integer | ClockControl::CLK_PDN_Power_Off,
ClockControl::CLK_IDRV_2mA | ClockControl::CLK_SRC_MS_Self | ClockControl::CLK_INV_Normal | ClockControl::MS_INT_Integer | ClockControl::CLK_PDN_Power_Off,
ClockControl::CLK_IDRV_6mA | ClockControl::CLK_SRC_MS_Self | ClockControl::CLK_INV_Normal | ClockControl::MS_INT_Integer | ClockControl::CLK_PDN_Power_Off,
};
constexpr ClockControls si5351_clock_control_xtal {
si5351_clock_control_common[0] | si5351_clock_control_ms_src_xtal,
si5351_clock_control_common[1] | si5351_clock_control_ms_src_xtal,
si5351_clock_control_common[2] | si5351_clock_control_ms_src_xtal,
si5351_clock_control_common[3] | si5351_clock_control_ms_src_xtal,
si5351_clock_control_common[4] | si5351_clock_control_ms_src_xtal,
si5351_clock_control_common[5] | si5351_clock_control_ms_src_xtal,
si5351_clock_control_common[6] | si5351_clock_control_ms_src_xtal,
si5351_clock_control_common[7] | si5351_clock_control_ms_src_xtal,
};
constexpr ClockControls si5351_clock_control_clkin {
si5351_clock_control_common[0] | si5351_clock_control_ms_src_clkin,
si5351_clock_control_common[1] | si5351_clock_control_ms_src_clkin,
si5351_clock_control_common[2] | si5351_clock_control_ms_src_clkin,
si5351_clock_control_common[3] | si5351_clock_control_ms_src_clkin,
si5351_clock_control_common[4] | si5351_clock_control_ms_src_clkin,
si5351_clock_control_common[5] | si5351_clock_control_ms_src_clkin,
si5351_clock_control_common[6] | si5351_clock_control_ms_src_clkin,
si5351_clock_control_common[7] | si5351_clock_control_ms_src_clkin,
};
void ClockManager::init() {
/* Must be sure to run the M4 core from IRC when messing with the signal
* generator that sources the GP_CLKIN signal that drives the micro-
* controller's PLL1 input.
*/
/* When booting from SPIFI, PLL1 is already running at 96MHz. */
//run_from_irc();
/* TODO: Refactor this blob, there's too much knowledge about post-boot
* state, which can change depending on where we're running from -- SPIFI
* or RAM or ???
*/
update_peripheral_clocks(cgu::CLK_SEL::IRC);
start_peripherals(cgu::CLK_SEL::IRC);
clock_generator.reset();
clock_generator.set_crystal_internal_load_capacitance(CrystalInternalLoadCapacitance::XTAL_CL_8pF);
clock_generator.enable_fanout();
clock_generator.set_pll_input_sources(si5351_pll_input_sources);
const bool use_clkin = false;
clock_generator.set_clock_control(
use_clkin ?
si5351_clock_control_clkin
: si5351_clock_control_xtal
);
clock_generator.write(si5351_pll_a_xtal_reg);
clock_generator.write(si5351_pll_b_clkin_reg);
clock_generator.write(si5351_ms_0_8m_reg);
clock_generator.write(si5351_ms_1_group_reg);
clock_generator.write(si5351_ms_2_group_reg);
clock_generator.write(si5351_ms_3_10m_reg);
clock_generator.write(si5351_ms_4_reg);
clock_generator.write(si5351_ms_5_reg);
clock_generator.write(si5351_ms6_7_off_mcu_clkin_reg);
clock_generator.reset_plls();
}
void ClockManager::run_from_irc() {
change_clock_configuration(cgu::CLK_SEL::IRC);
}
void ClockManager::run_at_full_speed() {
change_clock_configuration(cgu::CLK_SEL::PLL1);
}
void ClockManager::enable_codec_clocks() {
clock_generator.enable_clock(clock_generator_output_codec);
clock_generator.enable_clock(clock_generator_output_cpld);
clock_generator.enable_clock(clock_generator_output_sgpio);
/* Turn on all outputs at the same time. This probably doesn't ensure
* their phase relationships. For example, clocks that output frequencies
* in a 2:1 relationship may start with the slower clock high or low?
*/
clock_generator.enable_output_mask(
(1U << clock_generator_output_codec)
| (1U << clock_generator_output_cpld)
| (1U << clock_generator_output_sgpio)
);
}
void ClockManager::disable_codec_clocks() {
/* Turn off outputs before disabling clocks. It seems the clock needs to
* be enabled for the output to come to rest at the state specified by
* CLKx_DISABLE_STATE.
*/
clock_generator.disable_output_mask(
(1U << clock_generator_output_codec)
| (1U << clock_generator_output_cpld)
| (1U << clock_generator_output_sgpio)
);
clock_generator.disable_clock(clock_generator_output_codec);
clock_generator.disable_clock(clock_generator_output_cpld);
clock_generator.disable_clock(clock_generator_output_sgpio);
}
void ClockManager::enable_first_if_clock() {
clock_generator.enable_clock(clock_generator_output_first_if);
clock_generator.enable_output_mask(1U << clock_generator_output_first_if);
}
void ClockManager::disable_first_if_clock() {
clock_generator.disable_output_mask(1U << clock_generator_output_first_if);
clock_generator.disable_clock(clock_generator_output_first_if);
}
void ClockManager::enable_second_if_clock() {
clock_generator.enable_clock(clock_generator_output_second_if);
clock_generator.enable_output_mask(1U << clock_generator_output_second_if);
}
void ClockManager::disable_second_if_clock() {
clock_generator.disable_output_mask(1U << clock_generator_output_second_if);
clock_generator.disable_clock(clock_generator_output_second_if);
}
void ClockManager::set_sampling_frequency(const uint32_t frequency) {
/* Codec clock is at sampling frequency, CPLD and SGPIO clocks are at
* twice the frequency, and derived from the MS0 synth. So it's only
* necessary to change the MS0 synth frequency, and ensure the output
* is divided by two.
*/
clock_generator.set_ms_frequency(clock_generator_output_codec, frequency * 2, si5351_vco_f, 1);
}
void ClockManager::change_clock_configuration(const cgu::CLK_SEL clk_sel) {
/* If starting PLL1, turn on the clock feeding GP_CLKIN */
if( clk_sel == cgu::CLK_SEL::PLL1 ) {
enable_gp_clkin_source();
}
if( clk_sel == cgu::CLK_SEL::XTAL ) {
enable_xtal_oscillator();
}
stop_peripherals();
set_m4_clock_to_irc();
if( clk_sel == cgu::CLK_SEL::PLL1 ) {
set_m4_clock_to_pll1();
} else {
power_down_pll1();
}
update_peripheral_clocks(clk_sel);
start_peripherals(clk_sel);
if( clk_sel != cgu::CLK_SEL::XTAL ) {
disable_xtal_oscillator();
}
/* If not using PLL1, disable clock feeding GP_CLKIN */
if( clk_sel != cgu::CLK_SEL::PLL1 ) {
disable_gp_clkin_source();
}
}
void ClockManager::enable_gp_clkin_source() {
clock_generator.enable_clock(clock_generator_output_mcu_clkin);
clock_generator.enable_output(clock_generator_output_mcu_clkin);
}
void ClockManager::disable_gp_clkin_source() {
clock_generator.disable_clock(clock_generator_output_mcu_clkin);
clock_generator.disable_output(clock_generator_output_mcu_clkin);
}
void ClockManager::enable_xtal_oscillator() {
LPC_CGU->XTAL_OSC_CTRL.BYPASS = 0;
LPC_CGU->XTAL_OSC_CTRL.ENABLE = 1;
}
void ClockManager::disable_xtal_oscillator() {
LPC_CGU->XTAL_OSC_CTRL.ENABLE = 0;
}
void ClockManager::set_m4_clock_to_irc() {
/* Set M4 clock to safe default speed (~12MHz IRC) */
set_clock(LPC_CGU->BASE_M4_CLK, cgu::CLK_SEL::IRC);
systick_adjust_period(systick_count_irc);
//_clock_f = clock_source_irc_f;
halLPCSetSystemClock(clock_source_irc_f);
}
void ClockManager::set_m4_clock_to_pll1() {
/* Incantation from LPC43xx UM10503 section 12.2.1.1, to bring the M4
* core clock speed to the 110 - 204MHz range.
*/
/* Step into the 90-110MHz M4 clock range */
cgu::pll1::ctrl({
.pd = 0,
.bypass = 0,
.fbsel = 0,
.direct = 0,
.psel = 0,
.autoblock = 1,
.nsel = 0,
.msel = 4,
.clk_sel = cgu::CLK_SEL::GP_CLKIN,
});
while( !cgu::pll1::is_locked() );
/* Switch M4 clock to PLL1 running at intermediate rate */
set_clock(LPC_CGU->BASE_M4_CLK, cgu::CLK_SEL::PLL1);
systick_adjust_period(systick_count_pll1_step);
//_clock_f = clock_source_pll1_step_f;
halLPCSetSystemClock(clock_source_pll1_step_f);
/* Delay >50us at 90-110MHz clock speed */
volatile uint32_t delay = 1400;
while(delay--);
/* Remove /2P divider from PLL1 output to achieve full speed */
cgu::pll1::direct();
systick_adjust_period(systick_count_pll1);
//_clock_f = clock_source_pll1_f;
halLPCSetSystemClock(clock_source_pll1_f);
}
void ClockManager::power_down_pll1() {
/* Power down PLL1 if not needed */
cgu::pll1::disable();
}
void ClockManager::start_audio_pll() {
cgu::pll0audio::ctrl({
.pd = 1,
.bypass = 0,
.directi = 0,
.directo = 0,
.clken = 0,
.frm = 0,
.autoblock = 1,
.pllfract_req = 0,
.sel_ext = 1,
.mod_pd = 1,
.clk_sel = cgu::CLK_SEL::GP_CLKIN,
});
/* For 40MHz clock source, 48kHz audio rate, 256Fs MCLK:
* Fout=12.288MHz, Fcco=491.52MHz
* PSEL=20, NSEL=125, MSEL=768
* PDEC=31, NDEC=45, MDEC=30542
*/
cgu::pll0audio::mdiv({
.mdec = 30542,
});
cgu::pll0audio::np_div({
.pdec = 31,
.ndec = 45,
});
cgu::pll0audio::frac({
.pllfract_ctrl = 0,
});
cgu::pll0audio::power_up();
while( !cgu::pll0audio::is_locked() );
cgu::pll0audio::clock_enable();
set_clock(LPC_CGU->BASE_AUDIO_CLK, cgu::CLK_SEL::PLL0AUDIO);
}
void ClockManager::stop_audio_pll() {
cgu::pll0audio::clock_disable();
cgu::pll0audio::power_down();
while( cgu::pll0audio::is_locked() );
}
void ClockManager::stop_peripherals() {
i2c0.stop();
}
void ClockManager::update_peripheral_clocks(const cgu::CLK_SEL clk_sel) {
/* TODO: Extract a structure to represent clock settings for different
* modes.
*/
set_clock(LPC_CGU->BASE_PERIPH_CLK, clk_sel);
LPC_CGU->IDIVB_CTRL =
(0 << 1)
| (1 << 2)
| (1 << 11)
| (toUType(clk_sel) << 24)
;
set_clock(LPC_CGU->BASE_APB1_CLK, clk_sel);
set_clock(LPC_CGU->BASE_APB3_CLK, clk_sel);
set_clock(LPC_CGU->BASE_SDIO_CLK, clk_sel);
set_clock(LPC_CGU->BASE_SSP1_CLK, clk_sel);
}
void ClockManager::start_peripherals(const cgu::CLK_SEL clk_sel) {
/* Start APB1 peripherals considering new clock */
i2c0.start((clk_sel == cgu::CLK_SEL::PLL1)
? i2c_config_fast_clock
: i2c_config_slow_clock
);
}

View File

@ -0,0 +1,87 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __CLOCK_MANAGER_H__
#define __CLOCK_MANAGER_H__
#include "ch.h"
#include "hal.h"
#include "i2c_pp.hpp"
#include "si5351.hpp"
#include "lpc43xx_cpp.hpp"
using namespace lpc43xx;
class ClockManager {
public:
constexpr ClockManager(
I2C& i2c0,
si5351::Si5351& clock_generator
) : i2c0(i2c0),
clock_generator(clock_generator)/*,
_clock_f(0)*/
{
}
void init();
void run_from_irc();
void run_at_full_speed();
void start_audio_pll();
void stop_audio_pll();
void enable_codec_clocks();
void disable_codec_clocks();
void enable_first_if_clock();
void disable_first_if_clock();
void enable_second_if_clock();
void disable_second_if_clock();
void set_sampling_frequency(const uint32_t frequency);
private:
I2C& i2c0;
si5351::Si5351& clock_generator;
//uint32_t _clock_f;
void change_clock_configuration(const cgu::CLK_SEL clk_sel);
void enable_gp_clkin_source();
void disable_gp_clkin_source();
void enable_xtal_oscillator();
void disable_xtal_oscillator();
void set_m4_clock_to_irc();
void set_m4_clock_to_pll1();
void power_down_pll1();
void stop_peripherals();
void update_peripheral_clocks(const cgu::CLK_SEL clk_sel);
void start_peripherals(const cgu::CLK_SEL clk_sel);
};
#endif/*__CLOCK_MANAGER_H__*/

View File

@ -0,0 +1,84 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "cpld_update.hpp"
#include "portapack_hal.hpp"
#include "jtag_target_gpio.hpp"
#include "cpld_max5.hpp"
#include "portapack_cpld_data.hpp"
bool cpld_update_if_necessary() {
jtag::GPIOTarget target {
portapack::gpio_cpld_tck,
portapack::gpio_cpld_tms,
portapack::gpio_cpld_tdi,
portapack::gpio_cpld_tdo
};
jtag::JTAG jtag { target };
cpld::max5::CPLD cpld { jtag };
/* Unknown state */
cpld.reset();
cpld.run_test_idle();
/* Run-Test/Idle */
if( !cpld.idcode_ok() ) {
return false;
}
/* Enter ISP:
* Ensures that the I/O pins transition smoothly from user mode to ISP
* mode. All pins are tri-stated.
*/
cpld.enter_isp();
/* If silicon ID doesn't match, there's a serious problem. Leave CPLD
* in passive state.
*/
if( !cpld.silicon_id_ok() ) {
return false;
}
/* Verify CPLD contents against current bitstream. */
auto ok = cpld.verify(
portapack::cpld::block_0,
portapack::cpld::block_1
);
/* CPLD verifies incorrectly. Erase and program with current bitstream. */
if( !ok ) {
ok = cpld.program(
portapack::cpld::block_0,
portapack::cpld::block_1
);
}
/* If programming OK, reset CPLD to user mode. Otherwise leave it in
* passive (ISP) state.
*/
if( ok ) {
cpld.exit_isp();
}
return ok;
}

View File

@ -0,0 +1,27 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __CPLD_UPDATE_H__
#define __CPLD_UPDATE_H__
bool cpld_update_if_necessary();
#endif/*__CPLD_UPDATE_H__*/

View File

@ -0,0 +1,39 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "debounce.hpp"
#include "utility.hpp"
bool Debounce::feed(const uint8_t bit) {
history_ = (history_ << 1) | (bit & 1);
if( history_ == 0b00001111 ) {
state_ = 1;
return true;
}
if( history_ == 0b11110000 ) {
state_ = 0;
return true;
}
return false;
}

View File

@ -0,0 +1,40 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __DEBOUNCE_H__
#define __DEBOUNCE_H__
#include <cstdint>
class Debounce {
public:
bool feed(const uint8_t bit);
uint8_t state() const {
return state_;
}
private:
uint8_t history_ { 0 };
uint8_t state_ { 0 };
};
#endif/*__DEBOUNCE_H__*/

View File

@ -0,0 +1,64 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __DIRTY_REGISTERS_H__
#define __DIRTY_REGISTERS_H__
#include <cstddef>
#include <bitset>
#include "utility.hpp"
template<typename RegisterType, size_t RegisterCount>
class DirtyRegisters {
public:
using mask_t = std::bitset<RegisterCount>;
/* TODO: I feel like I might regret implementing this cast operator... */
operator bool() const {
return mask.any();
}
void set() {
mask.set();
}
void clear() {
mask.reset();
}
void clear(const size_t reg_num) {
mask.reset(reg_num);
}
typename mask_t::reference operator[](const size_t reg_num) {
return mask[reg_num];
}
typename mask_t::reference operator[](const RegisterType reg) {
return mask[toUType(reg)];
}
private:
mask_t mask;
};
#endif/*__DIRTY_REGISTERS_H__*/

View File

@ -0,0 +1,55 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "encoder.hpp"
#include "utility.hpp"
static const int8_t transition_map[] = {
0, // 0000: noop
0, // 0001: start
0, // 0010: start
0, // 0011: rate
1, // 0100: end
0, // 0101: noop
0, // 0110: rate
-1, // 0111: end
-1, // 1000: end
0, // 1001: rate
0, // 1010: noop
1, // 1011: end
0, // 1100: rate
0, // 1101: start
0, // 1110: start
0, // 1111: noop
};
int_fast8_t Encoder::update(
const uint_fast8_t phase_0,
const uint_fast8_t phase_1
) {
state <<= 1;
state |= phase_0;
state <<= 1;
state |= phase_1;
return transition_map[state & 0xf];
}

View File

@ -0,0 +1,38 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __ENCODER_H__
#define __ENCODER_H__
#include <cstdint>
class Encoder {
public:
int_fast8_t update(
const uint_fast8_t phase_0,
const uint_fast8_t phase_1
);
private:
uint_fast8_t state { 0 };
};
#endif/*__ENCODER_H__*/

View File

@ -0,0 +1,30 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "event.hpp"
#include "ch.h"
Thread* thread_event_loop = nullptr;
void events_initialize(Thread* const event_loop_thread) {
thread_event_loop = event_loop_thread;
}

View File

@ -0,0 +1,51 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __EVENT_H__
#define __EVENT_H__
#include "ch.h"
constexpr auto EVT_MASK_RTC_TICK = EVENT_MASK(0);
constexpr auto EVT_MASK_LCD_FRAME_SYNC = EVENT_MASK(1);
constexpr auto EVT_MASK_SD_CARD_PRESENT = EVENT_MASK(2);
constexpr auto EVT_MASK_SWITCHES = EVENT_MASK(3);
constexpr auto EVT_MASK_ENCODER = EVENT_MASK(4);
constexpr auto EVT_MASK_TOUCH = EVENT_MASK(5);
constexpr auto EVT_MASK_APPLICATION = EVENT_MASK(6);
void events_initialize(Thread* const event_loop_thread);
extern Thread* thread_event_loop;
inline void events_flag(const eventmask_t events) {
if( thread_event_loop ) {
chEvtSignal(thread_event_loop, events);
}
}
inline void events_flag_isr(const eventmask_t events) {
if( thread_event_loop ) {
chEvtSignalI(thread_event_loop, events);
}
}
#endif/*__EVENT_H__*/

View File

@ -0,0 +1,273 @@
/* CHIBIOS FIX */
#include "ch.h"
/*---------------------------------------------------------------------------/
/ FatFs - FAT file system module configuration file R0.10c (C)ChaN, 2014
/---------------------------------------------------------------------------*/
#define _FFCONF 80376 /* Revision ID */
/*---------------------------------------------------------------------------/
/ Functions and Buffer Configurations
/---------------------------------------------------------------------------*/
#define _FS_TINY 0
/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
/ At the tiny configuration, size of the file object (FIL) is reduced _MAX_SS
/ bytes. Instead of private sector buffer eliminated from the file object,
/ common sector buffer in the file system object (FATFS) is used for the file
/ data transfer. */
#define _FS_READONLY 0
/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
/ Read-only configuration removes basic writing API functions, f_write(),
/ f_sync(), f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(),
/ f_getfree() and optional writing functions as well. */
#define _FS_MINIMIZE 0
/* This option defines minimization level to remove some API functions.
/
/ 0: All basic functions are enabled.
/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_chmod(), f_utime(),
/ f_truncate() and f_rename() function are removed.
/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
/ 3: f_lseek() function is removed in addition to 2. */
#define _USE_STRFUNC 0
/* This option switches string functions, f_gets(), f_putc(), f_puts() and
/ f_printf().
/
/ 0: Disable string functions.
/ 1: Enable without LF-CRLF conversion.
/ 2: Enable with LF-CRLF conversion. */
#define _USE_MKFS 0
/* This option switches f_mkfs() function. (0:Disable or 1:Enable)
/ To enable it, also _FS_READONLY need to be set to 0. */
#define _USE_FASTSEEK 0
/* This option switches fast seek feature. (0:Disable or 1:Enable) */
#define _USE_LABEL 0
/* This option switches volume label functions, f_getlabel() and f_setlabel().
/ (0:Disable or 1:Enable) */
#define _USE_FORWARD 0
/* This option switches f_forward() function. (0:Disable or 1:Enable) */
/* To enable it, also _FS_TINY need to be set to 1. */
/*---------------------------------------------------------------------------/
/ Locale and Namespace Configurations
/---------------------------------------------------------------------------*/
#define _CODE_PAGE 1252
/* This option specifies the OEM code page to be used on the target system.
/ Incorrect setting of the code page can cause a file open failure.
/
/ 932 - Japanese Shift_JIS (DBCS, OEM, Windows)
/ 936 - Simplified Chinese GBK (DBCS, OEM, Windows)
/ 949 - Korean (DBCS, OEM, Windows)
/ 950 - Traditional Chinese Big5 (DBCS, OEM, Windows)
/ 1250 - Central Europe (Windows)
/ 1251 - Cyrillic (Windows)
/ 1252 - Latin 1 (Windows)
/ 1253 - Greek (Windows)
/ 1254 - Turkish (Windows)
/ 1255 - Hebrew (Windows)
/ 1256 - Arabic (Windows)
/ 1257 - Baltic (Windows)
/ 1258 - Vietnam (OEM, Windows)
/ 437 - U.S. (OEM)
/ 720 - Arabic (OEM)
/ 737 - Greek (OEM)
/ 775 - Baltic (OEM)
/ 850 - Multilingual Latin 1 (OEM)
/ 858 - Multilingual Latin 1 + Euro (OEM)
/ 852 - Latin 2 (OEM)
/ 855 - Cyrillic (OEM)
/ 866 - Russian (OEM)
/ 857 - Turkish (OEM)
/ 862 - Hebrew (OEM)
/ 874 - Thai (OEM, Windows)
/ 1 - ASCII (No extended character. Valid for only non-LFN configuration.) */
#define _USE_LFN 0
#define _MAX_LFN 255
/* The _USE_LFN option switches the LFN feature.
/
/ 0: Disable LFN feature. _MAX_LFN has no effect.
/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe.
/ 2: Enable LFN with dynamic working buffer on the STACK.
/ 3: Enable LFN with dynamic working buffer on the HEAP.
/
/ When enable the LFN feature, Unicode handling functions (option/unicode.c) must
/ be added to the project. The LFN working buffer occupies (_MAX_LFN + 1) * 2 bytes.
/ When use stack for the working buffer, take care on stack overflow. When use heap
/ memory for the working buffer, memory management functions, ff_memalloc() and
/ ff_memfree(), must be added to the project. */
#define _LFN_UNICODE 0
/* This option switches character encoding on the API. (0:ANSI/OEM or 1:Unicode)
/ To use Unicode string for the path name, enable LFN feature and set _LFN_UNICODE
/ to 1. This option also affects behavior of string I/O functions. */
#define _STRF_ENCODE 3
/* When _LFN_UNICODE is 1, this option selects the character encoding on the file to
/ be read/written via string I/O functions, f_gets(), f_putc(), f_puts and f_printf().
/
/ 0: ANSI/OEM
/ 1: UTF-16LE
/ 2: UTF-16BE
/ 3: UTF-8
/
/ When _LFN_UNICODE is 0, this option has no effect. */
#define _FS_RPATH 0
/* This option configures relative path feature.
/
/ 0: Disable relative path feature and remove related functions.
/ 1: Enable relative path feature. f_chdir() and f_chdrive() are available.
/ 2: f_getcwd() function is available in addition to 1.
/
/ Note that directory items read via f_readdir() are affected by this option. */
/*---------------------------------------------------------------------------/
/ Drive/Volume Configurations
/---------------------------------------------------------------------------*/
#define _VOLUMES 1
/* Number of volumes (logical drives) to be used. */
#define _STR_VOLUME_ID 0
#define _VOLUME_STRS "RAM","NAND","CF","SD1","SD2","USB1","USB2","USB3"
/* _STR_VOLUME_ID option switches string volume ID feature.
/ When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive
/ number in the path name. _VOLUME_STRS defines the drive ID strings for each
/ logical drives. Number of items must be equal to _VOLUMES. Valid characters for
/ the drive ID strings are: A-Z and 0-9. */
#define _MULTI_PARTITION 0
/* This option switches multi-partition feature. By default (0), each logical drive
/ number is bound to the same physical drive number and only an FAT volume found on
/ the physical drive will be mounted. When multi-partition feature is enabled (1),
/ each logical drive number is bound to arbitrary physical drive and partition
/ listed in the VolToPart[]. Also f_fdisk() funciton will be enabled. */
#define _MIN_SS 512
#define _MAX_SS 512
/* These options configure the range of sector size to be supported. (512, 1024,
/ 2048 or 4096) Always set both 512 for most systems, all type of memory cards and
/ harddisk. But a larger value may be required for on-board flash memory and some
/ type of optical media. When _MAX_SS is larger than _MIN_SS, FatFs is configured
/ to variable sector size and GET_SECTOR_SIZE command must be implemented to the
/ disk_ioctl() function. */
#define _USE_TRIM 0
/* This option switches ATA-TRIM feature. (0:Disable or 1:Enable)
/ To enable Trim feature, also CTRL_TRIM command should be implemented to the
/ disk_ioctl() function. */
#define _FS_NOFSINFO 0
/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
/ option, and f_getfree() function at first time after volume mount will force
/ a full FAT scan. Bit 1 controls the use of last allocated cluster number.
/
/ bit0=0: Use free cluster count in the FSINFO if available.
/ bit0=1: Do not trust free cluster count in the FSINFO.
/ bit1=0: Use last allocated cluster number in the FSINFO if available.
/ bit1=1: Do not trust last allocated cluster number in the FSINFO.
*/
/*---------------------------------------------------------------------------/
/ System Configurations
/---------------------------------------------------------------------------*/
#define _FS_NORTC 0
#define _NORTC_MON 11
#define _NORTC_MDAY 9
#define _NORTC_YEAR 2014
/* The _FS_NORTC option switches timestamp feature. If the system does not have
/ an RTC function or valid timestamp is not needed, set _FS_NORTC to 1 to disable
/ the timestamp feature. All objects modified by FatFs will have a fixed timestamp
/ defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR.
/ When timestamp feature is enabled (_FS_NORTC == 0), get_fattime() function need
/ to be added to the project to read current time form RTC. _NORTC_MON,
/ _NORTC_MDAY and _NORTC_YEAR have no effect.
/ These options have no effect at read-only configuration (_FS_READONLY == 1). */
#define _FS_LOCK 0
/* The _FS_LOCK option switches file lock feature to control duplicated file open
/ and illegal operation to open objects. This option must be 0 when _FS_READONLY
/ is 1.
/
/ 0: Disable file lock feature. To avoid volume corruption, application program
/ should avoid illegal open, remove and rename to the open objects.
/ >0: Enable file lock feature. The value defines how many files/sub-directories
/ can be opened simultaneously under file lock control. Note that the file
/ lock feature is independent of re-entrancy. */
#define _FS_REENTRANT 1
#define _FS_TIMEOUT 1000
#define _SYNC_t Semaphore *
/* The _FS_REENTRANT option switches the re-entrancy (thread safe) of the FatFs
/ module itself. Note that regardless of this option, file access to different
/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
/ and f_fdisk() function, are always not re-entrant. Only file/directory access
/ to the same volume is under control of this feature.
/
/ 0: Disable re-entrancy. _FS_TIMEOUT and _SYNC_t have no effect.
/ 1: Enable re-entrancy. Also user provided synchronization handlers,
/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
/ function, must be added to the project. Samples are available in
/ option/syscall.c.
/
/ The _FS_TIMEOUT defines timeout period in unit of time tick.
/ The _SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
/ SemaphoreHandle_t and etc.. */
#define _WORD_ACCESS 0
/* The _WORD_ACCESS option is an only platform dependent option. It defines
/ which access method is used to the word data on the FAT volume.
/
/ 0: Byte-by-byte access. Always compatible with all platforms.
/ 1: Word access. Do not choose this unless under both the following conditions.
/
/ * Address misaligned memory access is always allowed to ALL instructions.
/ * Byte order on the memory is little-endian.
/
/ If it is the case, _WORD_ACCESS can also be set to 1 to reduce code size.
/ Following table shows allowable settings of some processor types.
/
/ ARM7TDMI 0 ColdFire 0 V850E 0
/ Cortex-M3 0 Z80 0/1 V850ES 0/1
/ Cortex-M0 0 x86 0/1 TLCS-870 0/1
/ AVR 0/1 RX600(LE) 0/1 TLCS-900 0/1
/ AVR32 0 RL78 0 R32C 0
/ PIC18 0/1 SH-2 0 M16C 0/1
/ PIC24 0 H8S 0 MSP430 0
/ PIC32 0 H8/300H 0 8051 0/1
*/

313
firmware/application/halconf.h Executable file
View File

@ -0,0 +1,313 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
Copyright (C) 2014 Jared Boone, ShareBrained Technology
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file templates/halconf.h
* @brief HAL configuration header.
* @details HAL configuration file, this file allows to enable or disable the
* various device drivers from your application. You may also use
* this file in order to override the device drivers default settings.
*
* @addtogroup HAL_CONF
* @{
*/
#ifndef _HALCONF_H_
#define _HALCONF_H_
#include "mcuconf.h"
/**
* @brief Enables the TM subsystem.
*/
#if !defined(HAL_USE_TM) || defined(__DOXYGEN__)
#define HAL_USE_TM FALSE
#endif
/**
* @brief Enables the PAL subsystem.
*/
#if !defined(HAL_USE_PAL) || defined(__DOXYGEN__)
#define HAL_USE_PAL TRUE
#endif
/**
* @brief Enables the ADC subsystem.
*/
#if !defined(HAL_USE_ADC) || defined(__DOXYGEN__)
#define HAL_USE_ADC FALSE
#endif
/**
* @brief Enables the CAN subsystem.
*/
#if !defined(HAL_USE_CAN) || defined(__DOXYGEN__)
#define HAL_USE_CAN FALSE
#endif
/**
* @brief Enables the EXT subsystem.
*/
#if !defined(HAL_USE_EXT) || defined(__DOXYGEN__)
#define HAL_USE_EXT FALSE
#endif
/**
* @brief Enables the GPT subsystem.
*/
#if !defined(HAL_USE_GPT) || defined(__DOXYGEN__)
#define HAL_USE_GPT TRUE
#endif
/**
* @brief Enables the I2C subsystem.
*/
#if !defined(HAL_USE_I2C) || defined(__DOXYGEN__)
#define HAL_USE_I2C TRUE
#endif
/**
* @brief Enables the ICU subsystem.
*/
#if !defined(HAL_USE_ICU) || defined(__DOXYGEN__)
#define HAL_USE_ICU FALSE
#endif
/**
* @brief Enables the MAC subsystem.
*/
#if !defined(HAL_USE_MAC) || defined(__DOXYGEN__)
#define HAL_USE_MAC FALSE
#endif
/**
* @brief Enables the MMC_SPI subsystem.
*/
#if !defined(HAL_USE_MMC_SPI) || defined(__DOXYGEN__)
#define HAL_USE_MMC_SPI FALSE
#endif
/**
* @brief Enables the PWM subsystem.
*/
#if !defined(HAL_USE_PWM) || defined(__DOXYGEN__)
#define HAL_USE_PWM FALSE
#endif
/**
* @brief Enables the RTC subsystem.
*/
#if !defined(HAL_USE_RTC) || defined(__DOXYGEN__)
#define HAL_USE_RTC TRUE
#endif
/**
* @brief Enables the SDC subsystem.
*/
#if !defined(HAL_USE_SDC) || defined(__DOXYGEN__)
#define HAL_USE_SDC TRUE
#endif
/**
* @brief Enables the SERIAL subsystem.
*/
#if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__)
#define HAL_USE_SERIAL FALSE
#endif
/**
* @brief Enables the SERIAL over USB subsystem.
*/
#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)
#define HAL_USE_SERIAL_USB FALSE
#endif
/**
* @brief Enables the SPI subsystem.
*/
#if !defined(HAL_USE_SPI) || defined(__DOXYGEN__)
#define HAL_USE_SPI TRUE
#endif
/**
* @brief Enables the UART subsystem.
*/
#if !defined(HAL_USE_UART) || defined(__DOXYGEN__)
#define HAL_USE_UART FALSE
#endif
/**
* @brief Enables the USB subsystem.
*/
#if !defined(HAL_USE_USB) || defined(__DOXYGEN__)
#define HAL_USE_USB FALSE
#endif
/*===========================================================================*/
/* ADC driver related settings. */
/*===========================================================================*/
/**
* @brief Enables synchronous APIs.
* @note Disabling this option saves both code and data space.
*/
#if !defined(ADC_USE_WAIT) || defined(__DOXYGEN__)
#define ADC_USE_WAIT TRUE
#endif
/**
* @brief Enables the @p adcAcquireBus() and @p adcReleaseBus() APIs.
* @note Disabling this option saves both code and data space.
*/
#if !defined(ADC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
#define ADC_USE_MUTUAL_EXCLUSION TRUE
#endif
/*===========================================================================*/
/* CAN driver related settings. */
/*===========================================================================*/
/**
* @brief Sleep mode related APIs inclusion switch.
*/
#if !defined(CAN_USE_SLEEP_MODE) || defined(__DOXYGEN__)
#define CAN_USE_SLEEP_MODE TRUE
#endif
/*===========================================================================*/
/* I2C driver related settings. */
/*===========================================================================*/
/**
* @brief Enables the mutual exclusion APIs on the I2C bus.
*/
#if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
#define I2C_USE_MUTUAL_EXCLUSION TRUE
#endif
/*===========================================================================*/
/* MAC driver related settings. */
/*===========================================================================*/
/**
* @brief Enables an event sources for incoming packets.
*/
#if !defined(MAC_USE_ZERO_COPY) || defined(__DOXYGEN__)
#define MAC_USE_ZERO_COPY FALSE
#endif
/**
* @brief Enables an event sources for incoming packets.
*/
#if !defined(MAC_USE_EVENTS) || defined(__DOXYGEN__)
#define MAC_USE_EVENTS TRUE
#endif
/*===========================================================================*/
/* MMC_SPI driver related settings. */
/*===========================================================================*/
/**
* @brief Delays insertions.
* @details If enabled this options inserts delays into the MMC waiting
* routines releasing some extra CPU time for the threads with
* lower priority, this may slow down the driver a bit however.
* This option is recommended also if the SPI driver does not
* use a DMA channel and heavily loads the CPU.
*/
#if !defined(MMC_NICE_WAITING) || defined(__DOXYGEN__)
#define MMC_NICE_WAITING TRUE
#endif
/*===========================================================================*/
/* SDC driver related settings. */
/*===========================================================================*/
/**
* @brief Number of initialization attempts before rejecting the card.
* @note Attempts are performed at 10mS intervals.
*/
#if !defined(SDC_INIT_RETRY) || defined(__DOXYGEN__)
#define SDC_INIT_RETRY 100
#endif
/**
* @brief Include support for MMC cards.
* @note MMC support is not yet implemented so this option must be kept
* at @p FALSE.
*/
#if !defined(SDC_MMC_SUPPORT) || defined(__DOXYGEN__)
#define SDC_MMC_SUPPORT FALSE
#endif
/**
* @brief Delays insertions.
* @details If enabled this options inserts delays into the MMC waiting
* routines releasing some extra CPU time for the threads with
* lower priority, this may slow down the driver a bit however.
*/
#if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__)
#define SDC_NICE_WAITING TRUE
#endif
/*===========================================================================*/
/* SERIAL driver related settings. */
/*===========================================================================*/
/**
* @brief Default bit rate.
* @details Configuration parameter, this is the baud rate selected for the
* default configuration.
*/
#if !defined(SERIAL_DEFAULT_BITRATE) || defined(__DOXYGEN__)
#define SERIAL_DEFAULT_BITRATE 38400
#endif
/**
* @brief Serial buffers size.
* @details Configuration parameter, you can change the depth of the queue
* buffers depending on the requirements of your application.
* @note The default is 64 bytes for both the transmission and receive
* buffers.
*/
#if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__)
#define SERIAL_BUFFERS_SIZE 16
#endif
/*===========================================================================*/
/* SPI driver related settings. */
/*===========================================================================*/
/**
* @brief Enables synchronous APIs.
* @note Disabling this option saves both code and data space.
*/
#if !defined(SPI_USE_WAIT) || defined(__DOXYGEN__)
#define SPI_USE_WAIT TRUE
#endif
/**
* @brief Enables the @p spiAcquireBus() and @p spiReleaseBus() APIs.
* @note Disabling this option saves both code and data space.
*/
#if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
#define SPI_USE_MUTUAL_EXCLUSION TRUE
#endif
#endif /* _HALCONF_H_ */
/** @} */

View File

@ -0,0 +1,208 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "irq_controls.hpp"
#include "ch.h"
#include "hal.h"
#include "event.hpp"
#include "touch.hpp"
#include "touch_adc.hpp"
#include "encoder.hpp"
#include "debounce.hpp"
#include "utility.hpp"
#include <cstdint>
#include <array>
#include "portapack_io.hpp"
#include "hackrf_hal.hpp"
using namespace hackrf::one;
static std::array<Debounce, 7> switch_debounce;
static Encoder encoder;
static volatile uint32_t encoder_position { 0 };
static volatile uint32_t touch_phase { 0 };
/* TODO: Change how touch scanning works. It produces a decent amount of noise
* when changing potential on the resistive touch pad. Among other things, I
* see blips of noise when sampling the MAX2837 RSSI signal. And when the
* radio is off (RSSI signal is not driven?), the signal floats a LOT when the
* touch panel potentials are changing.
*
* Ideally, scan only for pressure until a touch is detected. Then scan X/Y.
* Noise will only occur when the panel is being touched. Not ideal, but
* an acceptable improvement.
*/
static std::array<portapack::IO::TouchPinsConfig, 11> touch_pins_configs {
/* State machine will pause here until touch is detected. */
portapack::IO::TouchPinsConfig::WaitTouch,
portapack::IO::TouchPinsConfig::SensePressure,
portapack::IO::TouchPinsConfig::SenseX,
portapack::IO::TouchPinsConfig::SenseY,
portapack::IO::TouchPinsConfig::SenseX,
portapack::IO::TouchPinsConfig::SenseY,
portapack::IO::TouchPinsConfig::SensePressure,
portapack::IO::TouchPinsConfig::SenseX,
portapack::IO::TouchPinsConfig::SenseY,
portapack::IO::TouchPinsConfig::SenseX,
portapack::IO::TouchPinsConfig::SenseY,
};
static touch::Frame temp_frame;
static touch::Frame touch_frame;
static uint32_t touch_debounce = 0;
static uint32_t touch_debounce_mask = (1U << 1) - 1;
static bool touch_stable = false;
static bool touch_update() {
const auto samples = touch::adc::get();
const auto current_phase = touch_pins_configs[touch_phase];
if( current_phase == portapack::IO::TouchPinsConfig::WaitTouch ) {
/* Debounce touches. */
const bool touch_raw = (samples.yp < touch::touch_threshold) && (samples.yn < touch::touch_threshold);
touch_debounce = (touch_debounce << 1) | (touch_raw ? 1U : 0U);
touch_stable = ((touch_debounce & touch_debounce_mask) == touch_debounce_mask);
} else {
if( touch_stable ) {
switch(current_phase) {
case portapack::IO::TouchPinsConfig::SensePressure:
temp_frame.pressure += samples;
break;
case portapack::IO::TouchPinsConfig::SenseX:
temp_frame.x += samples;
break;
case portapack::IO::TouchPinsConfig::SenseY:
temp_frame.y += samples;
break;
default:
break;
}
}
}
touch_phase++;
if( touch_phase >= touch_pins_configs.size() ) {
/* New iteration, calculate values and flag touch event */
touch_phase = 0;
temp_frame.touch = touch_stable;
touch_frame = temp_frame;
temp_frame = {};
return true;
} else {
return false;
}
}
static bool switches_update(const uint8_t switches_raw) {
// TODO: Only fire event on press, not release?
bool switch_changed = false;
for(size_t i=0; i<switch_debounce.size(); i++) {
switch_changed |= switch_debounce[i].feed((switches_raw >> i) & 1);
}
return switch_changed;
}
static bool encoder_read() {
const auto delta = encoder.update(
switch_debounce[5].state(),
switch_debounce[6].state()
);
if( delta != 0 ) {
encoder_position += delta;
return true;;
} else {
return false;
}
}
void timer0_callback(GPTDriver* const) {
eventmask_t event_mask = 0;
if( touch_update() ) event_mask |= EVT_MASK_TOUCH;
const auto switches_raw = portapack::io.io_update(touch_pins_configs[touch_phase]);
if( switches_update(switches_raw) ) {
event_mask |= EVT_MASK_SWITCHES;
if( encoder_read() ) event_mask |= EVT_MASK_ENCODER;
}
/* Signal event loop */
if( event_mask ) {
chSysLockFromIsr();
events_flag_isr(event_mask);
chSysUnlockFromIsr();
}
touch::adc::start();
}
/* TODO: Refactor some/all of this to appropriate shared headers? */
static constexpr uint32_t timer0_count_f = 1000000;
static constexpr uint32_t timer0_prescaler_ratio = (base_m0_clk_f / timer0_count_f);
static constexpr uint32_t ui_interrupt_rate = 1000;
static constexpr uint32_t timer0_match_count = timer0_count_f / ui_interrupt_rate;
/* GPT driver refers to configuration structure during runtime, so make sure
* it sticks around.
*/
static GPTConfig timer0_config {
.callback = timer0_callback,
.pr = timer0_prescaler_ratio - 1,
};
void controls_init() {
/* GPT timer 0 is used to scan user interface controls -- touch screen,
* navigation switches.
*/
gptStart(&GPTD1, &timer0_config);
gptStartContinuous(&GPTD1, timer0_match_count);
}
SwitchesState get_switches_state() {
SwitchesState result;
for(size_t i=0; i<result.size(); i++) {
// TODO: Ignore multiple keys at the same time?
result[i] = switch_debounce[i].state();
}
return result;
}
EncoderPosition get_encoder_position() {
return encoder_position;
}
touch::Frame get_touch_frame() {
return touch_frame;
}

View File

@ -0,0 +1,47 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __IRQ_CONTROLS_H__
#define __IRQ_CONTROLS_H__
#include <cstdint>
#include <bitset>
#include "touch.hpp"
enum class Switch {
Right = 0,
Left = 1,
Down = 2,
Up = 3,
Sel = 4,
};
using SwitchesState = std::bitset<5>;
using EncoderPosition = uint32_t;
void controls_init();
SwitchesState get_switches_state();
EncoderPosition get_encoder_position();
touch::Frame get_touch_frame();
#endif/*__IRQ_CONTROLS_H__*/

View File

@ -0,0 +1,50 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "irq_ipc.hpp"
#include "event.hpp"
#include "ch.h"
#include "hal.h"
#include "lpc43xx_cpp.hpp"
using namespace lpc43xx;
void m4txevent_interrupt_enable() {
nvicEnableVector(M4CORE_IRQn, CORTEX_PRIORITY_MASK(LPC43XX_M4TXEVENT_IRQ_PRIORITY));
}
extern "C" {
CH_IRQ_HANDLER(M4Core_IRQHandler) {
CH_IRQ_PROLOGUE();
chSysLockFromIsr();
events_flag_isr(EVT_MASK_APPLICATION);
chSysUnlockFromIsr();
creg::m4txevent::clear();
CH_IRQ_EPILOGUE();
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __IRQ_IPC_H__
#define __IRQ_IPC_H__
void m4txevent_interrupt_enable();
#endif/*__IRQ_IPC_H__*/

View File

@ -0,0 +1,65 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "irq_lcd_frame.hpp"
#include "event.hpp"
#include "ch.h"
#include "hal.h"
#include "portapack_hal.hpp"
static void pin_int4_interrupt_enable() {
nvicEnableVector(PIN_INT4_IRQn, CORTEX_PRIORITY_MASK(LPC43XX_PIN_INT4_IRQ_PRIORITY));
}
void lcd_frame_sync_configure() {
/* Positive edge sensitivity */
LPC_GPIO_INT->ISEL &= ~(1U << 4);
LPC_GPIO_INT->SIENR = (1U << 4);
LPC_GPIO_INT->CIENF = (1U << 4);
LPC_GPIO_INT->IST = (1U << 4);
LPC_SCU->PINTSEL1 =
(LPC_SCU->PINTSEL1 & ~(0xffU << 0))
| (portapack::gpio_lcd_te.pad() << 0)
| (portapack::gpio_lcd_te.port() << 5)
;
pin_int4_interrupt_enable();
}
extern "C" {
CH_IRQ_HANDLER(PIN_INT4_IRQHandler) {
CH_IRQ_PROLOGUE();
chSysLockFromIsr();
events_flag_isr(EVT_MASK_LCD_FRAME_SYNC);
chSysUnlockFromIsr();
LPC_GPIO_INT->IST = (1U << 4);
CH_IRQ_EPILOGUE();
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __IRQ_LCD_FRAME_H__
#define __IRQ_LCD_FRAME_H__
void lcd_frame_sync_configure();
#endif/*__IRQ_LCD_FRAME_H__*/

View File

@ -0,0 +1,53 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "m4_startup.hpp"
#include "hal.h"
#include <cstdint>
#include <cstddef>
#include <cstring>
static constexpr uint32_t m4_text_flash_image_offset = 0x10000;
static constexpr size_t m4_text_size = 0x8000;
static constexpr uint32_t m4_text_flash_base = LPC_SPIFI_DATA_CACHED_BASE + m4_text_flash_image_offset;
static constexpr uint32_t m4_text_ram_base = 0x10080000;
/* TODO: OK, this is cool, but how do I put the M4 to sleep so I can switch to
* a different image? Other than asking the old image to sleep while the M0
* makes changes?
*
* I suppose I could force M4MEMMAP to an invalid memory reason which would
* cause an exception and effectively halt the M4. But that feels gross.
*/
void m4_init() {
/* Initialize M4 code RAM */
std::memcpy((void*)m4_text_ram_base, (void*)m4_text_flash_base, m4_text_size);
/* M4 core is assumed to be sleeping with interrupts off, so we can mess
* with its address space and RAM without concern.
*/
LPC_CREG->M4MEMMAP = m4_text_ram_base;
/* Reset M4 core */
LPC_RGU->RESET_CTRL[0] = (1 << 13);
}

View File

@ -0,0 +1,27 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __M4_STARTUP_H__
#define __M4_STARTUP_H__
void m4_init();
#endif/*__M4_STARTUP_H__*/

671
firmware/application/main.cpp Executable file
View File

@ -0,0 +1,671 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ch.h"
#include "test.h"
#include "hackrf_hal.hpp"
#include "hackrf_gpio.hpp"
using namespace hackrf::one;
#include "portapack_shared_memory.hpp"
#include "portapack_hal.hpp"
#include "portapack_io.hpp"
#include "cpld_update.hpp"
#include "message_queue.hpp"
#include "si5351.hpp"
#include "clock_manager.hpp"
#include "wm8731.hpp"
#include "radio.hpp"
#include "touch.hpp"
#include "touch_adc.hpp"
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_painter.hpp"
#include "ui_navigation.hpp"
#include "receiver_model.hpp"
#include "irq_ipc.hpp"
#include "irq_lcd_frame.hpp"
#include "irq_controls.hpp"
#include "event.hpp"
#include "i2c_pp.hpp"
#include "spi_pp.hpp"
#include "m4_startup.hpp"
#include "debug.hpp"
#include "led.hpp"
#include "gcc.hpp"
#include <string.h>
I2C i2c0(&I2CD0);
SPI ssp0(&SPID1);
SPI ssp1(&SPID2);
wolfson::wm8731::WM8731 audio_codec { i2c0, portapack::wm8731_i2c_address };
/* From ChibiOS crt0.c:
* Two stacks available for Cortex-M, main stack or process stack.
*
* Thread mode: Used to execute application software. The processor
* enters Thread mode when it comes out of reset.
* Handler mode: Used to handle exceptions. The processor returns to
* Thread mode when it has finished all exception processing.
*
* ChibiOS configures the Cortex-M in dual-stack mode. (CONTROL[1]=1)
* When CONTROL[1]=1, PSP is used when the processor is in Thread mode.
*
* MSP is always used when the processor is in Handler mode.
*
* __main_stack_size__ : 0x2000???? - 0x2000???? =
* Used for exception handlers. Yes, really.
* __process_stack_size__ : 0x2000???? - 0x2000???? =
* Used by main().
*
* After chSysInit(), the current instructions stream (usually main())
* becomes the main thread.
*/
#if 0
static const SPIConfig ssp_config_w25q80bv = {
.end_cb = NULL,
.ssport = ?,
.sspad = ?,
.cr0 =
CR0_CLOCKRATE()
| ?
| ?
,
.cpsr = ?,
};
static spi_bus_t ssp0 = {
.obj = &SPID1,
.config = &ssp_config_w25q80bv,
.start = spi_chibi_start,
.stop = spi_chibi_stop,
.transfer = spi_chibi_transfer,
.transfer_gather = spi_chibi_transfer_gather,
};
#endif
/* ChibiOS initialization sequence:
* ResetHandler:
* Initialize FPU (if present)
* Initialize stacks (fill with pattern)
* __early_init()
* Enable extra processor exceptions for debugging
* Init data segment (flash -> data)
* Initialize BSS (fill with 0)
* __late_init()
* reset_peripherals()
* halInit()
* hal_lld_init()
* Init timer 3 as cycle counter
* Init RIT as SysTick
* palInit()
* gptInit()
* i2cInit()
* sdcInit()
* spiInit()
* rtcInit()
* boardInit()
* chSysInit()
* Constructors
* main()
* Destructors
* _default_exit() (default is infinite loop)
*/
si5351::Si5351 clock_generator {
i2c0, si5351_i2c_address
};
ClockManager clock_manager {
i2c0, clock_generator
};
ReceiverModel receiver_model {
clock_manager
};
class Power {
public:
void init() {
/* VAA powers:
* MAX5864 analog section.
* MAX2837 registers and other functions.
* RFFC5072 analog section.
*
* Beware that power applied to pins of the MAX2837 may
* show up on VAA and start powering other components on the
* VAA net. So turn on VAA before driving pins from MCU to
* MAX2837.
*/
/* Turn on VAA */
gpio_vaa_disable.clear();
gpio_vaa_disable.output();
/* 1V8 powers CPLD internals.
*/
/* Turn on 1V8 */
gpio_1v8_enable.set();
gpio_1v8_enable.output();
/* Set VREGMODE for switching regulator on HackRF One */
gpio_vregmode.set();
gpio_vregmode.output();
}
private:
};
static Power power;
static void init() {
for(const auto& pin : pins) {
pin.init();
}
/* Configure other pins */
LPC_SCU->SFSI2C0 =
(1U << 3)
| (1U << 11)
;
power.init();
gpio_max5864_select.set();
gpio_max5864_select.output();
gpio_max2837_select.set();
gpio_max2837_select.output();
led_usb.setup();
led_rx.setup();
led_tx.setup();
clock_manager.init();
clock_manager.run_at_full_speed();
clock_manager.start_audio_pll();
audio_codec.init();
clock_manager.enable_first_if_clock();
clock_manager.enable_second_if_clock();
clock_manager.enable_codec_clocks();
radio::init();
touch::adc::init();
}
extern "C" {
void __late_init(void) {
reset();
/*
* System initializations.
* - HAL initialization, this also initializes the configured device drivers
* and performs the board-specific initializations.
* - Kernel initialization, the main() function becomes a thread and the
* RTOS is active.
*/
halInit();
/* After this call, scheduler, systick, heap, etc. are available. */
/* By doing chSysInit() here, it runs before C++ constructors, which may
* require the heap.
*/
chSysInit();
}
}
extern "C" {
CH_IRQ_HANDLER(RTC_IRQHandler) {
CH_IRQ_PROLOGUE();
chSysLockFromIsr();
events_flag_isr(EVT_MASK_RTC_TICK);
chSysUnlockFromIsr();
rtc::interrupt::clear_all();
CH_IRQ_EPILOGUE();
}
}
static bool ui_dirty = true;
void ui::dirty_event() {
ui_dirty = true;
}
class EventDispatcher {
public:
EventDispatcher(
ui::Widget* const top_widget,
ui::Painter& painter,
ui::Context& context
) : top_widget { top_widget },
painter { painter },
context { context }
{
// touch_manager.on_started = [this](const ui::TouchEvent event) {
// this->context.focus_manager.update(this->top_widget, event);
// };
touch_manager.on_event = [this](const ui::TouchEvent event) {
this->on_touch_event(event);
};
}
eventmask_t wait() {
return chEvtWaitAny(ALL_EVENTS);
}
void dispatch(const eventmask_t events) {
if( events & EVT_MASK_APPLICATION ) {
handle_application_queue();
}
if( events & EVT_MASK_RTC_TICK ) {
handle_rtc_tick();
}
if( events & EVT_MASK_LCD_FRAME_SYNC ) {
handle_lcd_frame_sync();
}
if( events & EVT_MASK_SD_CARD_PRESENT ) {
handle_sd_card_detect();
}
if( events & EVT_MASK_SWITCHES ) {
handle_switches();
}
if( events & EVT_MASK_ENCODER ) {
handle_encoder();
}
if( events & EVT_MASK_TOUCH ) {
handle_touch();
}
}
private:
touch::Manager touch_manager;
ui::Widget* const top_widget;
ui::Painter& painter;
ui::Context& context;
uint32_t encoder_last = 0;
void handle_application_queue() {
while( !shared_memory.application_queue.is_empty() ) {
auto message = shared_memory.application_queue.pop();
auto& fn = context.message_map[message->id];
if( fn ) {
fn(message);
}
message->state = Message::State::Free;
}
}
void handle_rtc_tick() {
/*
if( shared_memory.application_queue.push(&rssi_request) ) {
led_rx.on();
}
*/
/*
if( callback_second_tick ) {
rtc::RTC datetime;
rtcGetTime(&RTCD1, &datetime);
callback_second_tick(datetime);
}
*/
//static std::function<void(size_t app_n, size_t baseband_n)> callback_fifos_state;
//static std::function<void(systime_t ticks)> callback_cpu_ticks;
/*
if( callback_fifos_state ) {
callback_fifos_state(shared_memory.application_queue.len(), baseband_queue.len());
}
*/
/*
if( callback_cpu_ticks ) {
//const auto thread_self = chThdSelf();
const auto thread = chSysGetIdleThread();
//const auto ticks = chThdGetTicks(thread);
callback_cpu_ticks(thread->total_ticks);
}
*/
/*
callback_fifos_state = [&system_view](size_t app_n, size_t baseband_n) {
system_view.status_view.text_app_fifo_n.set(
ui::to_string_dec_uint(app_n, 3)
);
system_view.status_view.text_baseband_fifo_n.set(
ui::to_string_dec_uint(baseband_n, 3)
);
};
*/
/*
callback_cpu_ticks = [&system_view](systime_t ticks) {
static systime_t last_ticks = 0;
const auto delta_ticks = ticks - last_ticks;
last_ticks = ticks;
const auto text_pct = ui::to_string_dec_uint(delta_ticks / 2000000, 3) + "% idle";
system_view.status_view.text_ticks.set(
text_pct
);
};
*/
}
/*
void paint_widget(ui::Widget* const w) {
if( w->visible() ) {
if( w->dirty() ) {
w->paint(painter);
// Force-paint all children.
for(const auto child : w->children()) {
child->set_dirty();
paint_widget(child);
}
w->set_clean();
} else {
// Selectively paint all children.
for(const auto child : w->children()) {
paint_widget(child);
}
}
}
}
*/
void paint_widget(ui::Widget* const w) {
if( w->hidden() ) {
// Mark widget (and all children) as invisible.
w->visible(false);
} else {
// Mark this widget as visible and recurse.
w->visible(true);
if( w->dirty() ) {
w->paint(painter);
// Force-paint all children.
for(const auto child : w->children()) {
child->set_dirty();
paint_widget(child);
}
w->set_clean();
} else {
// Selectively paint all children.
for(const auto child : w->children()) {
paint_widget(child);
}
}
}
}
static ui::Widget* touch_widget(ui::Widget* const w, ui::TouchEvent event) {
if( !w->hidden() ) {
// To achieve reverse depth ordering (last object drawn is
// considered "top"), descend first.
for(const auto child : w->children()) {
const auto touched_widget = touch_widget(child, event);
if( touched_widget ) {
return touched_widget;
}
}
const auto r = w->screen_rect();
if( r.contains(event.point) ) {
if( w->on_touch(event) ) {
// This widget responded. Return it up the call stack.
return w;
}
}
}
return nullptr;
}
ui::Widget* captured_widget { nullptr };
void on_touch_event(ui::TouchEvent event) {
/* TODO: Capture widget receiving the Start event, send Move and
* End events to the same widget.
*/
/* Capture Start widget.
* If touch is over Start widget at Move event, then the widget
* should be highlighted. If the touch is not over the Start
* widget at Move event, widget should un-highlight.
* If touch is over Start widget at End event, then the widget
* action should occur.
*/
if( event.type == ui::TouchEvent::Type::Start ) {
captured_widget = touch_widget(this->top_widget, event);
}
if( captured_widget ) {
captured_widget->on_touch(event);
}
}
void handle_lcd_frame_sync() {
if( ui_dirty ) {
paint_widget(top_widget);
ui_dirty = false;
}
}
void handle_sd_card_detect() {
}
void handle_switches() {
const auto switches_state = get_switches_state();
for(size_t i=0; i<switches_state.size(); i++) {
// TODO: Ignore multiple keys at the same time?
if( switches_state[i] ) {
const auto event = static_cast<ui::KeyEvent>(i);
if( !event_bubble_key(event) ) {
context.focus_manager.update(top_widget, event);
}
}
}
}
void handle_encoder() {
const uint32_t encoder_now = get_encoder_position();
const int32_t delta = static_cast<int32_t>(encoder_now - encoder_last);
encoder_last = encoder_now;
const auto event = static_cast<ui::EncoderEvent>(delta);
event_bubble_encoder(event);
}
void handle_touch() {
touch_manager.feed(get_touch_frame());
}
bool event_bubble_key(const ui::KeyEvent event) {
auto target = context.focus_manager.focus_widget();
while( (target != nullptr) && !target->on_key(event) ) {
target = target->parent();
}
/* Return true if event was consumed. */
return (target != nullptr);
}
void event_bubble_encoder(const ui::EncoderEvent event) {
auto target = context.focus_manager.focus_widget();
while( (target != nullptr) && !target->on_encoder(event) ) {
target = target->parent();
}
}
};
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
/* Thinking things through a bit:
main() produces UI events.
Touch events:
Hit test entire screen hierarchy and send to hit widget.
If modal view shown, block UI events destined outside.
Navigation events:
Move from current focus widget to "nearest" focusable widget.
If current view is modal, don't allow events to bubble outside
of modal view.
System events:
Power off from WWDT provides enough time to flush changes to
VBAT RAM?
SD card events? Insert/eject/error.
View stack:
Views that are hidden should deconstruct their widgets?
Views that are shown after being hidden will reconstruct their
widgets from data in their model?
Hence, hidden views will not eat up memory beyond their model?
Beware loops where the stack can get wildly deep?
Breaking out data models from views should allow some amount of
power-off persistence in the VBAT RAM area. In fact, the data
models could be instantiated there? But then, how to protect
from corruption if power is pulled? Does WWDT provide enough
warning to flush changes?
Navigation...
If you move off the left side of the screen, move to breadcrumb
"back" item, no matter where you're coming from?
*/
/*
message_handlers[Message::ID::FSKPacket] = [](const Message* const p) {
const auto message = static_cast<const FSKPacketMessage*>(p);
fsk_packet(message);
};
message_handlers[Message::ID::TestResults] = [&system_view](const Message* const p) {
const auto message = static_cast<const TestResultsMessage*>(p);
char c[10];
c[0] = message->results.translate_by_fs_over_4_and_decimate_by_2_cic3 ? '+' : '-';
c[1] = message->results.fir_cic3_decim_2_s16_s16 ? '+' : '-';
c[2] = message->results.fir_64_and_decimate_by_2_complex ? '+' : '-';
c[3] = message->results.fxpt_atan2 ? '+' : '-';
c[4] = message->results.multiply_conjugate_s16_s32 ? '+' : '-';
c[5] = 0;
system_view.status_view.portapack.set(c);
};
*/
portapack::IO portapack::io {
portapack::gpio_dir,
portapack::gpio_lcd_rd,
portapack::gpio_lcd_wr,
portapack::gpio_io_stbx,
portapack::gpio_addr,
portapack::gpio_lcd_te,
portapack::gpio_unused,
};
int main(void) {
init();
if( !cpld_update_if_necessary() ) {
chSysHalt();
}
init_message_queues();
portapack::io.init();
ui::Context context;
context.display.init();
sdcStart(&SDCD1, nullptr);
rtc::interrupt::enable_second_inc();
nvicEnableVector(RTC_IRQn, CORTEX_PRIORITY_MASK(LPC_RTC_IRQ_PRIORITY));
controls_init();
lcd_frame_sync_configure();
events_initialize(chThdSelf());
ui::SystemView system_view {
context,
{ 0, 0, 240, 320 }
};
ui::Painter painter { context.display };
EventDispatcher event_dispatcher { &system_view, painter, context };
context.message_map[Message::ID::FSKPacket] = [](const Message* const p) {
const auto message = static_cast<const FSKPacketMessage*>(p);
(void)message;
led_usb.toggle();
};
m4txevent_interrupt_enable();
m4_init();
while(true) {
const auto events = event_dispatcher.wait();
event_dispatcher.dispatch(events);
}
return 0;
}
void debug_indicate_error_init() {
// TODO: Indicate M4 error.
// led_usb.off();
// led_tx.off();
}
void debug_indicate_error_update() {
// TODO: Indicate M4 error.
// led_usb.toggle();
// led_tx.toggle();
}

View File

@ -0,0 +1,254 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "max2837.hpp"
#include "hackrf_hal.hpp"
#include "hackrf_gpio.hpp"
using namespace hackrf::one;
#include "ch.h"
#include "hal.h"
#include <algorithm>
namespace max2837 {
namespace lna {
constexpr std::array<uint8_t, 8> lookup_8db_steps {
0b111, 0b011, 0b110, 0b010,
0b100, 0b000, 0b000, 0b000
};
static uint_fast8_t gain_ordinal(const int8_t db) {
int8_t db_sat = std::min(std::max(gain_db_min, db), gain_db_max);
return lna::lookup_8db_steps[(db_sat >> 3) & 7];
}
} /* namespace lna */
namespace vga {
static uint_fast8_t gain_ordinal(const int8_t db) {
int8_t db_sat = std::min(std::max(gain_db_min, db), gain_db_max);
return ((db_sat >> 1) & 0b11111) ^ 0b11111;
}
} /* namespace vga */
namespace filter {
static uint_fast8_t bandwidth_ordinal(const uint32_t bandwidth) {
/* Determine filter setting that will provide bandwidth greater than or
* equal to requested bandwidth.
*/
return std::lower_bound(bandwidths.cbegin(), bandwidths.cend(), bandwidth) - bandwidths.cbegin();
}
} /* namespace filter */
/* Empirical testing indicates about 25us is necessary to get a valid
* temperature sense conversion from the ADC.
*/
constexpr float seconds_for_temperature_sense_adc_conversion = 30.0e-6;
constexpr halrtcnt_t ticks_for_temperature_sense_adc_conversion = (base_m4_clk_f * seconds_for_temperature_sense_adc_conversion + 1);
constexpr uint32_t reference_frequency = max2837_reference_f;
constexpr uint32_t pll_factor = 1.0 / (4.0 / 3.0 / reference_frequency) + 0.5;
void MAX2837::init() {
set_mode(Mode::Shutdown);
gpio_max2837_enable.output();
gpio_max2837_rxenable.output();
gpio_max2837_txenable.output();
_map.r.tx_gain.TXVGA_GAIN_SPI_EN = 1;
_map.r.tx_gain.TXVGA_GAIN_MSB_SPI_EN = 1;
_map.r.tx_gain.TXVGA_GAIN_SPI = 0x00;
_map.r.lpf_3_vga_1.VGAMUX_enable = 1;
_map.r.lpf_3_vga_1.VGA_EN = 1;
_map.r.hpfsm_3.HPC_STOP = 1; /* 1kHz */
_map.r.rx_top_rx_bias.LNAgain_SPI_EN = 1; /* control LNA gain from SPI */
_map.r.rxrf_2.L = 0b000;
_map.r.rx_top_rx_bias.VGAgain_SPI_EN = 1; /* control VGA gain from SPI */
_map.r.vga_2.VGA = 0b01010;
_map.r.lpf_3_vga_1.BUFF_VCM = 0b00; /* TODO: Check values out of ADC */
_map.r.lpf_1.LPF_EN = 1; /* Enable low-pass filter */
_map.r.lpf_1.ModeCtrl = 0b01; /* Rx LPF */
_map.r.lpf_1.FT = 0b0000; /* 5MHz LPF */
_map.r.spi_en.EN_SPI = 1; /* enable chip functions when ENABLE pin set */
_map.r.lo_gen.LOGEN_2GM = 0;
#if 0
_map.r.rxrf_1.LNA_EN = 1;
_map.r.rxrf_1.Mixer_EN = 1;
_map.r.rxrf_1.RxLO_EN = 1;
_map.r.rx_top.DOUT_DRVH = 0; /* slow down DOUT edges */
_map.r.hpfsm_4.DOUT_CSB_SEL = 0; /* DOUT not tri-stated, is independent of CSB */
_map.r.xtal_cfg.XTAL_CLKOUT_EN = 0; /* CLKOUT pin disabled. (Seems to have no effect.) */
#endif
_map.r.vga_3_rx_top.RSSI_EN_SPIenables = 1;
_map.r.vga_3_rx_top.RSSI_MODE = 1; /* RSSI independent of RXHP */
_dirty.set();
flush();
}
void MAX2837::set_mode(const Mode mode) {
gpio_max2837_enable.write(toUType(mode) & toUType(Mode::Mask_Enable));
gpio_max2837_rxenable.write(toUType(mode) & toUType(Mode::Mask_RxEnable));
gpio_max2837_txenable.write(toUType(mode) & toUType(Mode::Mask_TxEnable));
}
void MAX2837::flush() {
if( _dirty ) {
for(size_t n=0; n<reg_count; n++) {
if( _dirty[n] ) {
write(n, _map.w[n]);
}
}
_dirty.clear();
}
}
void MAX2837::flush_one(const Register reg) {
const auto reg_num = toUType(reg);
write(reg_num, _map.w[reg_num]);
_dirty.clear(reg_num);
}
inline void MAX2837::write(const address_t reg_num, const reg_t value) {
uint16_t t = (0U << 15) | (reg_num << 10) | (value & 0x3ffU);
_target.transfer(&t, 1);
}
inline reg_t MAX2837::read(const address_t reg_num) {
uint16_t t = (1U << 15) | (reg_num << 10);
_target.transfer(&t, 1U);
return t & 0x3ffU;
}
inline void MAX2837::write(const Register reg, const reg_t value) {
write(toUType(reg), value);
}
inline reg_t MAX2837::read(const Register reg) {
return read(toUType(reg));
}
void MAX2837::set_tx_vga_gain(const int_fast8_t value) {
_map.r.tx_gain.TXVGA_GAIN_SPI = value;
_dirty[Register::TX_GAIN] = 1;
flush();
}
void MAX2837::set_lna_gain(const int_fast8_t db) {
_map.r.rxrf_2.L = lna::gain_ordinal(db);
_dirty[Register::RXRF_2] = 1;
flush();
}
void MAX2837::set_vga_gain(const int_fast8_t db) {
_map.r.vga_2.VGA = vga::gain_ordinal(db);
_dirty[Register::VGA_2] = 1;
flush();
}
void MAX2837::set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum) {
_map.r.lpf_1.FT = filter::bandwidth_ordinal(bandwidth_minimum);
_dirty[Register::LPF_1] = 1;
flush();
}
bool MAX2837::set_frequency(const rf::Frequency lo_frequency) {
/* TODO: This is a sad implementation. Refactor. */
if( lo_frequency < lo::band[0].min ) {
return false;
} else if( lo_frequency < lo::band[0].max ) {
_map.r.syn_int_div.LOGEN_BSW = 0b00; /* 2300 - 2399.99MHz */
_map.r.rxrf_1.LNAband = 0; /* 2.3 - 2.5GHz */
} else if( lo_frequency < lo::band[1].max ) {
_map.r.syn_int_div.LOGEN_BSW = 0b01; /* 2400 - 2499.99MHz */
_map.r.rxrf_1.LNAband = 0; /* 2.3 - 2.5GHz */
} else if( lo_frequency < lo::band[2].max ) {
_map.r.syn_int_div.LOGEN_BSW = 0b10; /* 2500 - 2599.99MHz */
_map.r.rxrf_1.LNAband = 1; /* 2.5 - 2.7GHz */
} else if( lo_frequency < lo::band[3].max ) {
_map.r.syn_int_div.LOGEN_BSW = 0b11; /* 2600 - 2700Hz */
_map.r.rxrf_1.LNAband = 1; /* 2.5 - 2.7GHz */
} else {
return false;
}
_dirty[Register::SYN_INT_DIV] = 1;
_dirty[Register::RXRF_1] = 1;
const uint64_t div_q20 = (lo_frequency * (1 << 20)) / pll_factor;
_map.r.syn_int_div.SYN_INTDIV = div_q20 >> 20;
_dirty[Register::SYN_INT_DIV] = 1;
_map.r.syn_fr_div_2.SYN_FRDIV_19_10 = (div_q20 >> 10) & 0x3ff;
_dirty[Register::SYN_FR_DIV_2] = 1;
/* flush to commit high FRDIV first, as low FRDIV commits the change */
flush();
_map.r.syn_fr_div_1.SYN_FRDIV_9_0 = (div_q20 & 0x3ff);
_dirty[Register::SYN_FR_DIV_1] = 1;
flush();
return true;
}
reg_t MAX2837::temp_sense() {
if( !_map.r.rx_top.ts_en ) {
_map.r.rx_top.ts_en = 1;
flush_one(Register::RX_TOP);
chThdSleepMilliseconds(1);
}
_map.r.rx_top.ts_adc_trigger = 1;
flush_one(Register::RX_TOP);
halPolledDelay(ticks_for_temperature_sense_adc_conversion);
const auto value = read(Register::TEMP_SENSE);
_map.r.rx_top.ts_adc_trigger = 0;
flush_one(Register::RX_TOP);
return value;
}
}

View File

@ -0,0 +1,902 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __MAX2837_H__
#define __MAX2837_H__
#include "gpio.hpp"
#include "spi_arbiter.hpp"
#include <cstdint>
#include <array>
#include "dirty_registers.hpp"
#include "rf_path.hpp"
#include "utility.hpp"
namespace max2837 {
enum class Mode {
Mask_Enable = 0b001,
Mask_RxEnable = 0b010,
Mask_TxEnable = 0b100,
Shutdown = 0b000,
Standby = Mask_Enable,
Receive = Mask_Enable | Mask_RxEnable,
Transmit = Mask_Enable | Mask_TxEnable,
};
/*************************************************************************/
namespace lo {
constexpr std::array<rf::FrequencyRange, 4> band { {
{ .min = 2300000000, .max = 2400000000, },
{ .min = 2400000000, .max = 2500000000, },
{ .min = 2500000000, .max = 2600000000, },
{ .min = 2600000000, .max = 2700000000, },
} };
} /* namespace lo */
/*************************************************************************/
namespace lna {
constexpr int8_t gain_db_min = 0;
constexpr int8_t gain_db_max = 40;
constexpr int8_t gain_db_step = 8;
constexpr std::array<rf::FrequencyRange, 2> band { {
{ .min = 2300000000, .max = 2500000000, },
{ .min = 2500000000, .max = 2700000000, },
} };
} /* namespace lna */
/*************************************************************************/
namespace vga {
constexpr int8_t gain_db_min = 0;
constexpr int8_t gain_db_max = 62;
constexpr int8_t gain_db_step = 2;
} /* namespace vga */
/*************************************************************************/
namespace filter {
constexpr std::array<uint32_t, 16> bandwidths {
/* Assumption: these values are in ascending order */
1750000,
2500000, /* Some documentation says 2.25MHz */
3500000,
5000000,
5500000,
6000000,
7000000,
8000000,
9000000,
10000000,
12000000,
14000000,
15000000,
20000000,
24000000,
28000000,
};
constexpr auto bandwidth_minimum = bandwidths[0];
constexpr auto bandwidth_maximum = bandwidths[bandwidths.size() - 1];
} /* namespace filter */
/*************************************************************************/
using reg_t = uint16_t;
using address_t = uint8_t;
constexpr size_t reg_count = 32;
enum class Register : address_t {
RXRF_1 = 0,
RXRF_2 = 1,
LPF_1 = 2,
LPF_2 = 3,
LPF_3_VGA_1 = 4,
VGA_2 = 5,
VGA_3_RX_TOP = 6,
TEMP_SENSE = 7,
RX_TOP_RX_BIAS = 8,
RX_TOP = 9,
TX_TOP_1 = 10,
TX_TOP_2 = 11,
HPFSM_1 = 12,
HPFSM_2 = 13,
HPFSM_3 = 14,
HPFSM_4 = 15,
SPI_EN = 16,
SYN_FR_DIV_1 = 17,
SYN_FR_DIV_2 = 18,
SYN_INT_DIV = 19,
SYN_CFG_1 = 20,
SYN_CFG_2 = 21,
VAS_CFG = 22,
LO_MISC = 23,
XTAL_CFG = 24,
VCO_CFG = 25,
LO_GEN = 26,
PA_DRV_PA_DAC = 27,
PA_DAC = 28,
TX_GAIN = 29,
TX_LO_IQ = 30,
TX_DC_CORR = 31,
};
struct RXRF_1_Type {
reg_t LNA_EN : 1;
reg_t Mixer_EN : 1;
reg_t RxLO_EN : 1;
reg_t Lbias : 2;
reg_t Mbias : 2;
reg_t buf : 2;
reg_t LNAband : 1;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(RXRF_1_Type) == sizeof(reg_t), "RXRF_1_Type wrong size");
struct RXRF_2_Type {
reg_t LNAtune : 1;
reg_t LNAde_Q : 1;
reg_t L : 3;
reg_t iqerr_trim : 5;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(RXRF_2_Type) == sizeof(reg_t), "RXRF_2_Type wrong size");
struct LPF_1_Type {
reg_t LPF_EN : 1;
reg_t TxBB_EN : 1;
reg_t ModeCtrl : 2;
reg_t FT : 4;
reg_t dF : 2;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(LPF_1_Type) == sizeof(reg_t), "LPF_1_Type wrong size");
struct LPF_2_Type {
reg_t PT_SPI : 4;
reg_t Bqd : 3;
reg_t TxRPCM : 3;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(LPF_2_Type) == sizeof(reg_t), "LPF_2_Type wrong size");
struct LPF_3_VGA_1_Type {
reg_t RP : 2;
reg_t TxBuff : 2;
reg_t VGA_EN : 1;
reg_t VGAMUX_enable : 1;
reg_t BUFF_Curr : 2;
reg_t BUFF_VCM : 2;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(LPF_3_VGA_1_Type) == sizeof(reg_t), "LPF_3_VGA_1_Type wrong size");
struct VGA_2_Type {
reg_t VGA : 5;
reg_t sel_In1_In2 : 1;
reg_t turbo15n20 : 1;
reg_t VGA_Curr : 2;
reg_t fuse_arm : 1;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(VGA_2_Type) == sizeof(reg_t), "VGA_2_Type wrong size");
struct VGA_3_RX_TOP_Type {
reg_t RESERVED0 : 6;
reg_t RSSI_EN_SPIenables : 1;
reg_t RSSI_MUX : 1;
reg_t RSSI_MODE : 1;
reg_t LPF_MODE_SEL : 1;
reg_t RESERVED1 : 6;
};
static_assert(sizeof(VGA_3_RX_TOP_Type) == sizeof(reg_t), "VGA_3_RX_TOP_Type wrong size");
struct TEMP_SENSE_Type {
reg_t ts_adc : 5;
reg_t RESERVED0 : 1;
reg_t PLL_test_output : 1;
reg_t VAS_test_output : 1;
reg_t HPFSM_test_output : 1;
reg_t LOGEN_trim_divider_test_output : 1;
reg_t RESERVED1 : 6;
};
static_assert(sizeof(TEMP_SENSE_Type) == sizeof(reg_t), "TEMP_SENSE_Type wrong size");
struct RX_TOP_RX_BIAS_Type {
reg_t LNAgain_SPI_EN : 1;
reg_t VGAgain_SPI_EN : 1;
reg_t EN_Bias_Trim : 1;
reg_t BIAS_TRIM_SPI : 5;
reg_t BIAS_TRIM_CNTRL : 1;
reg_t RX_IQERR_SPI_EN : 1;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(RX_TOP_RX_BIAS_Type) == sizeof(reg_t), "RX_TOP_RX_BIAS_Type wrong size");
struct RX_TOP_Type {
reg_t ts_adc_trigger : 1;
reg_t ts_en : 1;
reg_t LPFtrim_SPI_EN : 1;
reg_t DOUT_DRVH : 1;
reg_t DOUT_PU : 1;
reg_t DOUT_SEL : 3;
reg_t fuse_th : 1;
reg_t fuse_burn_gkt : 1;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(RX_TOP_Type) == sizeof(reg_t), "RX_TOP_Type wrong size");
struct TX_TOP_1_Type {
reg_t RESERVED0 : 1;
reg_t TXCAL_GAIN : 2;
reg_t TXCAL_V2I_FILT : 3;
reg_t TX_BIAS_ADJ : 2;
reg_t RESERVED1 : 2;
reg_t RESERVED2 : 6;
};
static_assert(sizeof(TX_TOP_1_Type) == sizeof(reg_t), "TX_TOP_1_Type wrong size");
struct TX_TOP_2_Type {
reg_t AMD_SPI_EN : 1;
reg_t TXMXR_V2I_GAIN : 4;
reg_t RESERVED0 : 5;
reg_t RESERVED1 : 6;
};
static_assert(sizeof(TX_TOP_2_Type) == sizeof(reg_t), "TX_TOP_2_Type wrong size");
struct HPFSM_1_Type {
reg_t HPC_10M : 2;
reg_t HPC_10M_GAIN : 2;
reg_t HPC_600k : 3;
reg_t HPC_600k_GAIN : 3;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(HPFSM_1_Type) == sizeof(reg_t), "HPFSM_1_Type wrong size");
struct HPFSM_2_Type {
reg_t HPC_100k : 2;
reg_t HPC_100k_GAIN : 2;
reg_t HPC_30k : 2;
reg_t HPC_30k_GAIN : 2;
reg_t HPC_1k : 2;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(HPFSM_2_Type) == sizeof(reg_t), "HPFSM_2_Type wrong size");
struct HPFSM_3_Type {
reg_t HPC_1k_GAIN : 2;
reg_t HPC_DELAY : 2;
reg_t HPC_STOP : 2;
reg_t HPC_STOP_M2 : 2;
reg_t HPC_RXGAIN_EN : 1;
reg_t HPC_MODE : 1;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(HPFSM_3_Type) == sizeof(reg_t), "HPFSM_3_Type wrong size");
struct HPFSM_4_Type {
reg_t HPC_DIVH : 1;
reg_t HPC_TST : 5;
reg_t HPC_SEQ_BYP : 1;
reg_t DOUT_CSB_SEL : 1;
reg_t RESERVED0 : 2;
reg_t RESERVED1 : 6;
};
static_assert(sizeof(HPFSM_4_Type) == sizeof(reg_t), "HPFSM_4_Type wrong size");
struct SPI_EN_Type {
reg_t EN_SPI : 1;
reg_t CAL_SPI : 1;
reg_t LOGEN_SPI_EN : 1;
reg_t SYN_SPI_EN : 1;
reg_t VAS_SPI_EN : 1;
reg_t PADRV_SPI_EN : 1;
reg_t PADAC_SPI_EN : 1;
reg_t PADAC_TX_EN : 1;
reg_t TXMX_SPI_EN : 1;
reg_t TXLO_SPI_EN : 1;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(SPI_EN_Type) == sizeof(reg_t), "SPI_EN_Type wrong size");
struct SYN_FR_DIV_1_Type {
reg_t SYN_FRDIV_9_0 : 10;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(SYN_FR_DIV_1_Type) == sizeof(reg_t), "SYN_FR_DIV_1_Type wrong size");
struct SYN_FR_DIV_2_Type {
reg_t SYN_FRDIV_19_10 : 10;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(SYN_FR_DIV_2_Type) == sizeof(reg_t), "SYN_FR_DIV_2_Type wrong size");
struct SYN_INT_DIV_Type {
reg_t SYN_INTDIV : 8;
reg_t LOGEN_BSW : 2;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(SYN_INT_DIV_Type) == sizeof(reg_t), "SYN_INT_DIV_Type wrong size");
struct SYN_CFG_1_Type {
reg_t SYN_MODE_FR_EN : 1;
reg_t SYN_REF_DIV_RATIO : 2;
reg_t SYN_CP_CURRENT : 2;
reg_t SYN_CLOCKOUT_DRIVE : 1;
reg_t SYN_TURBO_EN : 1;
reg_t CP_TRM_SET : 1;
reg_t CP_TRM_CODE : 2;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(SYN_CFG_1_Type) == sizeof(reg_t), "SYN_CFG_1_Type wrong size");
struct SYN_CFG_2_Type {
reg_t SYN_CP_COMMON_MODE_EN : 1;
reg_t SYN_PRESCALER_BIAS_BOOST : 1;
reg_t SYN_CP_BETA_CURRENT_COMP_EN : 1;
reg_t SYN_SD_CLK_SEL : 1;
reg_t SYN_CP_PW_ADJ : 1;
reg_t SYN_CP_LIN_CURRENT_SEL : 2;
reg_t SYN_TEST_OUT_SEL : 3;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(SYN_CFG_2_Type) == sizeof(reg_t), "SYN_CFG_2_Type wrong size");
struct VAS_CFG_Type {
reg_t VAS_MODE : 1;
reg_t VAS_RELOCK_SEL : 1;
reg_t VAS_DIV : 3;
reg_t VAS_DLY : 2;
reg_t VAS_TRIG_EN : 1;
reg_t VAS_ADE : 1;
reg_t VAS_ADL_SPI : 1;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(VAS_CFG_Type) == sizeof(reg_t), "VAS_CFG_Type wrong size");
struct LO_MISC_Type {
reg_t VAS_SPI : 5;
reg_t XTAL_BIAS_SEL : 2;
reg_t XTAL_E2C_BIAS_SEL : 1;
reg_t VAS_SE : 1;
reg_t VCO_SPI_EN : 1;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(LO_MISC_Type) == sizeof(reg_t), "LO_MISC_Type wrong size");
struct XTAL_CFG_Type {
reg_t XTAL_FTUNE : 7;
reg_t XTAL_CLKOUT_EN : 1;
reg_t XTAL_CLKOUT_DIV : 1;
reg_t XTAL_CORE_EN : 1;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(XTAL_CFG_Type) == sizeof(reg_t), "XTAL_CFG_Type wrong size");
struct VCO_CFG_Type {
reg_t VCO_BIAS_SPI_EN : 1;
reg_t VCO_BIAS_SPI : 4;
reg_t VCO_CMEN : 1;
reg_t VCO_PDET_TST : 2;
reg_t VCO_BUF_BIASH : 2;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(VCO_CFG_Type) == sizeof(reg_t), "VCO_CFG_Type wrong size");
struct LO_GEN_Type {
reg_t LOGEN_BIASH1 : 2;
reg_t LOGEN_BIASH2 : 1;
reg_t LOGEN_2GM : 1;
reg_t LOGEN_TRIM1 : 1;
reg_t LOGEN_TRIM2 : 1;
reg_t VAS_TST : 4;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(LO_GEN_Type) == sizeof(reg_t), "LO_GEN_Type wrong size");
struct PA_DRV_PA_DAC_Type {
reg_t PADRV_BIAS : 3;
reg_t PADRV_DOWN_SPI_EN : 1;
reg_t PADRV_DOWN_SPI_SEL : 1;
reg_t PADAC_IV : 1;
reg_t PADAC_VMODE : 1;
reg_t PADAC_DIVH : 1;
reg_t TXGATE_EN : 1;
reg_t TX_DCCORR_EN : 1;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(PA_DRV_PA_DAC_Type) == sizeof(reg_t), "PA_DRV_PA_DAC_Type wrong size");
struct PA_DAC_Type {
reg_t PADAC_BIAS : 6;
reg_t PADAC_DLY : 4;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(PA_DAC_Type) == sizeof(reg_t), "PA_DAC_Type wrong size");
struct TX_GAIN_Type {
reg_t TXVGA_GAIN_SPI_EN : 1;
reg_t TXVGA_GAIN_MSB_SPI_EN : 1;
reg_t TX_DCCORR_SPI_EN : 1;
reg_t FUSE_ARM : 1;
reg_t TXVGA_GAIN_SPI : 6;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(TX_GAIN_Type) == sizeof(reg_t), "TX_GAIN_Type wrong size");
struct TX_LO_IQ_Type {
reg_t TXLO_IQ_SPI : 5;
reg_t TXLO_IQ_SPI_EN : 1;
reg_t TXLO_BUFF : 2;
reg_t FUSE_GKT : 1;
reg_t FUSE_RTH : 1;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(TX_LO_IQ_Type) == sizeof(reg_t), "TX_LO_IQ_Type wrong size");
struct TX_DC_CORR_Type {
reg_t TX_DCCORR_I : 5;
reg_t TX_DCCORR_Q : 5;
reg_t RESERVED0 : 6;
};
static_assert(sizeof(TX_DC_CORR_Type) == sizeof(reg_t), "TX_DC_CORR_Type wrong size");
struct Register_Type {
RXRF_1_Type rxrf_1; /* 0 */
RXRF_2_Type rxrf_2;
LPF_1_Type lpf_1;
LPF_2_Type lpf_2;
LPF_3_VGA_1_Type lpf_3_vga_1; /* 4 */
VGA_2_Type vga_2;
VGA_3_RX_TOP_Type vga_3_rx_top;
TEMP_SENSE_Type temp_sense;
RX_TOP_RX_BIAS_Type rx_top_rx_bias; /* 8 */
RX_TOP_Type rx_top;
TX_TOP_1_Type tx_top_1;
TX_TOP_2_Type tx_top_2;
HPFSM_1_Type hpfsm_1; /* 12 */
HPFSM_2_Type hpfsm_2;
HPFSM_3_Type hpfsm_3;
HPFSM_4_Type hpfsm_4;
SPI_EN_Type spi_en; /* 16 */
SYN_FR_DIV_1_Type syn_fr_div_1;
SYN_FR_DIV_2_Type syn_fr_div_2;
SYN_INT_DIV_Type syn_int_div;
SYN_CFG_1_Type syn_cfg_1; /* 20 */
SYN_CFG_2_Type syn_cfg_2;
VAS_CFG_Type vas_cfg;
LO_MISC_Type lo_misc;
XTAL_CFG_Type xtal_cfg; /* 24 */
VCO_CFG_Type vco_cfg;
LO_GEN_Type lo_gen;
PA_DRV_PA_DAC_Type pa_drv_pa_dac;
PA_DAC_Type pa_dac; /* 28 */
TX_GAIN_Type tx_gain;
TX_LO_IQ_Type tx_lo_iq;
TX_DC_CORR_Type tx_dc_corr;
};
static_assert(sizeof(Register_Type) == reg_count * sizeof(reg_t), "Register_Type wrong size");
struct RegisterMap {
constexpr RegisterMap(
Register_Type values
) : r(values)
{
}
union {
Register_Type r;
std::array<reg_t, reg_count> w;
};
};
static_assert(sizeof(RegisterMap) == reg_count * sizeof(reg_t), "RegisterMap type wrong size");
constexpr RegisterMap initial_register_values { Register_Type {
/* Best effort to reconcile default values specified in three
* different places in the MAX2837 documentation.
*/
.rxrf_1 = { /* 0 */
.LNA_EN = 0,
.Mixer_EN = 0,
.RxLO_EN = 0,
.Lbias = 0b10,
.Mbias = 0b10,
.buf = 0b10,
.LNAband = 0,
.RESERVED0 = 0,
},
.rxrf_2 = { /* 1 */
.LNAtune = 0,
.LNAde_Q = 1,
.L = 0b000,
.iqerr_trim = 0b0000,
.RESERVED0 = 0,
},
.lpf_1 = { /* 2 */
.LPF_EN = 0,
.TxBB_EN = 0,
.ModeCtrl = 0b01,
.FT = 0b1111,
.dF = 0b01,
.RESERVED0 = 0,
},
.lpf_2 = { /* 3 */
.PT_SPI = 0b1001,
.Bqd = 0b011,
.TxRPCM = 0b011,
.RESERVED0 = 0,
},
.lpf_3_vga_1 = { /* 4 */
.RP = 0b10,
.TxBuff = 0b10,
.VGA_EN = 0,
.VGAMUX_enable = 0,
.BUFF_Curr = 0b00,
.BUFF_VCM = 0b00,
.RESERVED0 = 0,
},
.vga_2 = { /* 5 */
.VGA = 0b00000,
.sel_In1_In2 = 0,
.turbo15n20 = 0,
.VGA_Curr = 0b01,
.fuse_arm = 0,
.RESERVED0 = 0,
},
.vga_3_rx_top = { /* 6 */
.RESERVED0 = 0b000110,
.RSSI_EN_SPIenables = 0,
.RSSI_MUX = 0,
.RSSI_MODE = 0,
.LPF_MODE_SEL = 0,
.RESERVED1 = 0,
},
.temp_sense = { /* 7 */
.ts_adc = 0b00000,
.RESERVED0 = 0,
.PLL_test_output = 0,
.VAS_test_output = 0,
.HPFSM_test_output = 0,
.LOGEN_trim_divider_test_output = 0,
.RESERVED1 = 0,
},
.rx_top_rx_bias = { /* 8 */
.LNAgain_SPI_EN = 0,
.VGAgain_SPI_EN = 0,
.EN_Bias_Trim = 0,
.BIAS_TRIM_SPI = 0b10000,
.BIAS_TRIM_CNTRL = 0,
.RX_IQERR_SPI_EN = 0,
.RESERVED0 = 0,
},
.rx_top = { /* 9 */
.ts_adc_trigger = 0,
.ts_en = 0,
.LPFtrim_SPI_EN = 0,
.DOUT_DRVH = 1, /* Documentation mismatch */
.DOUT_PU = 1,
.DOUT_SEL = 0b000,
.fuse_th = 0,
.fuse_burn_gkt = 0,
.RESERVED0 = 0,
},
.tx_top_1 = { /* 10 */
.RESERVED0 = 0,
.TXCAL_GAIN = 0b00,
.TXCAL_V2I_FILT = 0b011,
.TX_BIAS_ADJ = 0b01,
.RESERVED1 = 0b00,
.RESERVED2 = 0,
},
.tx_top_2 = { /* 11 */
.AMD_SPI_EN = 0,
.TXMXR_V2I_GAIN = 0b1011,
.RESERVED0 = 0b00000,
.RESERVED1 = 0,
},
.hpfsm_1 = { /* 12 */
.HPC_10M = 0b11,
.HPC_10M_GAIN = 0b11,
.HPC_600k = 0b100,
.HPC_600k_GAIN = 0b100,
.RESERVED0 = 0,
},
.hpfsm_2 = { /* 13 */
.HPC_100k = 0b00,
.HPC_100k_GAIN = 0b00,
.HPC_30k = 0b01,
.HPC_30k_GAIN = 0b01,
.HPC_1k = 0b01,
.RESERVED0 = 0,
},
.hpfsm_3 = { /* 14 */
.HPC_1k_GAIN = 0b01,
.HPC_DELAY = 0b01,
.HPC_STOP = 0b00,
.HPC_STOP_M2 = 0b11,
.HPC_RXGAIN_EN = 1,
.HPC_MODE = 0,
.RESERVED0 = 0,
},
.hpfsm_4 = { /* 15 */
.HPC_DIVH = 1,
.HPC_TST = 0b00000,
.HPC_SEQ_BYP = 0,
.DOUT_CSB_SEL = 1, /* Documentation mismatch */
.RESERVED0 = 0b00,
.RESERVED1 = 0,
},
.spi_en = { /* 16 */
.EN_SPI = 0,
.CAL_SPI = 0,
.LOGEN_SPI_EN = 1,
.SYN_SPI_EN = 1,
.VAS_SPI_EN = 1,
.PADRV_SPI_EN = 0,
.PADAC_SPI_EN = 0,
.PADAC_TX_EN = 0,
.TXMX_SPI_EN = 0,
.TXLO_SPI_EN = 0,
.RESERVED0 = 0,
},
.syn_fr_div_1 = { /* 17 */
.SYN_FRDIV_9_0 = 0b0101010101,
.RESERVED0 = 0,
},
.syn_fr_div_2 = { /* 18 */
.SYN_FRDIV_19_10 = 0b0101010101,
.RESERVED0 = 0,
},
.syn_int_div = { /* 19 */
.SYN_INTDIV = 0b01010011,
.LOGEN_BSW = 0b01,
.RESERVED0 = 0,
},
.syn_cfg_1 = { /* 20 */
.SYN_MODE_FR_EN = 1,
.SYN_REF_DIV_RATIO = 0b00,
.SYN_CP_CURRENT = 0b00,
.SYN_CLOCKOUT_DRIVE = 0,
.SYN_TURBO_EN = 1,
.CP_TRM_SET = 0,
.CP_TRM_CODE = 0b10,
.RESERVED0 = 0,
},
.syn_cfg_2 = { /* 21 */
.SYN_CP_COMMON_MODE_EN = 1,
.SYN_PRESCALER_BIAS_BOOST = 0,
.SYN_CP_BETA_CURRENT_COMP_EN = 1,
.SYN_SD_CLK_SEL = 1,
.SYN_CP_PW_ADJ = 0,
.SYN_CP_LIN_CURRENT_SEL = 0b01,
.SYN_TEST_OUT_SEL = 0b000,
.RESERVED0 = 0,
},
.vas_cfg = { /* 22 */
.VAS_MODE = 1,
.VAS_RELOCK_SEL = 0,
.VAS_DIV = 0b010,
.VAS_DLY = 0b01,
.VAS_TRIG_EN = 1,
.VAS_ADE = 1,
.VAS_ADL_SPI = 0,
.RESERVED0 = 0,
},
.lo_misc = { /* 23 */
.VAS_SPI = 0b01111,
.XTAL_BIAS_SEL = 0b10,
.XTAL_E2C_BIAS_SEL = 0,
.VAS_SE = 0,
.VCO_SPI_EN = 1,
.RESERVED0 = 0,
},
.xtal_cfg = { /* 24 */
.XTAL_FTUNE = 0b0000000,
.XTAL_CLKOUT_EN = 0, /* 1->0 to "turn off" CLKOUT pin. Doesn't seem to work though... */
.XTAL_CLKOUT_DIV = 1,
.XTAL_CORE_EN = 0,
.RESERVED0 = 0,
},
.vco_cfg = { /* 25 */
.VCO_BIAS_SPI_EN = 0,
.VCO_BIAS_SPI = 0b0000,
.VCO_CMEN = 0,
.VCO_PDET_TST = 0b00,
.VCO_BUF_BIASH = 0b01,
.RESERVED0 = 0,
},
.lo_gen = { /* 26 */
.LOGEN_BIASH1 = 0b10,
.LOGEN_BIASH2 = 0,
.LOGEN_2GM = 1,
.LOGEN_TRIM1 = 0,
.LOGEN_TRIM2 = 0,
.VAS_TST = 0b1111,
.RESERVED0 = 0,
},
.pa_drv_pa_dac = { /* 27 */
.PADRV_BIAS = 0b011,
.PADRV_DOWN_SPI_EN = 0,
.PADRV_DOWN_SPI_SEL = 0, /* Documentation mismatch */
.PADAC_IV = 1,
.PADAC_VMODE = 1,
.PADAC_DIVH = 1,
.TXGATE_EN = 1,
.TX_DCCORR_EN = 1,
.RESERVED0 = 0,
},
.pa_dac = { /* 28 */
.PADAC_BIAS = 0b000000,
.PADAC_DLY = 0b0011,
.RESERVED0 = 0,
},
.tx_gain = { /* 29 */
.TXVGA_GAIN_SPI_EN = 0,
.TXVGA_GAIN_MSB_SPI_EN = 0,
.TX_DCCORR_SPI_EN = 0,
.FUSE_ARM = 0,
.TXVGA_GAIN_SPI = 0b111111,
.RESERVED0 = 0,
},
.tx_lo_iq = { /* 30 */
.TXLO_IQ_SPI = 0b00000,
.TXLO_IQ_SPI_EN = 0,
.TXLO_BUFF = 0b10,
.FUSE_GKT = 0,
.FUSE_RTH = 0,
.RESERVED0 = 0,
},
.tx_dc_corr = { /* 31 */
.TX_DCCORR_I = 0b00000,
.TX_DCCORR_Q = 0b00000,
.RESERVED0 = 0,
},
} };
class MAX2837 {
public:
constexpr MAX2837(
spi::arbiter::Target& target
) : _target(target)
{
}
void init();
void set_mode(const Mode mode);
void set_tx_vga_gain(const int_fast8_t value);
void set_lna_gain(const int_fast8_t db);
void set_vga_gain(const int_fast8_t db);
void set_lpf_rf_bandwidth(const uint32_t bandwidth_minimum);
#if 0
void rx_cal() {
_map.r.spi_en.EN_SPI = 1;
_map.r.spi_en.CAL_SPI = 1;
flush_one(Register::SPI_EN);
_map.r.vga_3_rx_top.LPF_MODE_SEL = 1;
flush_one(Register::VGA_3_RX_TOP);
_map.r.lpf_1.ModeCtrl = 0b00;
flush_one(Register::LPF_1);
_map.r.lo_gen.LOGEN_2GM = 1;
flush_one(Register::LO_GEN);
chThdSleepMilliseconds(100);
_map.r.spi_en.CAL_SPI = 0;
flush_one(Register::SPI_EN);
_map.r.vga_3_rx_top.LPF_MODE_SEL = 0;
flush_one(Register::VGA_3_RX_TOP);
_map.r.lpf_1.ModeCtrl = 0b01;
flush_one(Register::LPF_1);
_map.r.lo_gen.LOGEN_2GM = 0;
flush_one(Register::LO_GEN);
}
void test_rx_offset(const size_t n) {
_map.r.hpfsm_4.HPC_TST = n;
_dirty[Register::HPFSM_4] = 1;
/*
_map.r.hpfsm_3.HPC_STOP = n;
_dirty[Register::HPFSM_3] = 1;
*/
flush();
}
#endif
bool set_frequency(const rf::Frequency lo_frequency);
reg_t temp_sense();
private:
spi::arbiter::Target& _target;
RegisterMap _map { initial_register_values };
DirtyRegisters<Register, reg_count> _dirty;
void flush_one(const Register reg);
void write(const address_t reg_num, const reg_t value);
reg_t read(const address_t reg_num);
void write(const Register reg, const reg_t value);
reg_t read(const Register reg);
void flush();
};
}
#endif/*__MAX2837_H__*/

View File

@ -0,0 +1,36 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "max5864.hpp"
#include <cstdint>
#include <array>
#include "utility.hpp"
namespace max5864 {
void MAX5864::set_mode(const Mode mode) {
std::array<uint8_t, 1> command { toUType(mode) };
_target.transfer(command.data(), command.size());
}
}

View File

@ -0,0 +1,59 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __MAX5864_H__
#define __MAX5864_H__
#include "spi_arbiter.hpp"
namespace max5864 {
enum class Mode : uint8_t {
Shutdown = 0x00,
Idle = 0x01,
Receive = 0x02,
Transmit = 0x03,
Transceiver = 0x04,
Standby = 0x05,
};
class MAX5864 {
public:
constexpr MAX5864(
spi::arbiter::Target& target
) : _target(target)
{
}
void init() {
/* Shut down explicitly, as there is no other reset mechanism. */
set_mode(Mode::Shutdown);
}
void set_mode(const Mode mode);
private:
spi::arbiter::Target& _target;
};
}
#endif/*__MAX5864_H__*/

69
firmware/application/mcuconf.h Executable file
View File

@ -0,0 +1,69 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
Copyright (C) 2014 Jared Boone, ShareBrained Technology
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
* LPC43xx drivers configuration.
* The following settings override the default settings present in
* the various device driver implementation headers.
* Note that the settings for each driver only have effect if the driver
* is enabled in halconf.h.
*
* IRQ priorities:
* 3...0 Lowest...highest.
*/
/* NOTE: Beware setting IRQ priorities < "2":
* dbg_check_enter_isr "#SV8 means that probably you have some IRQ set at a
* priority level above the kernel level (level 0 or 1 usually) so it is able
* to preempt the kernel and mess things up.
*/
/*
* I2C driver system settings.
*/
#define LPC43XX_I2C_USE_I2C0 TRUE
#define LPC43XX_I2C_USE_I2C1 TRUE
/*
* SPI driver system settings.
*/
#define LPC_SPI_USE_SSP0 TRUE
#define LPC_SPI_USE_SSP1 TRUE
/*
* DMA driver system settings.
*/
#define LPC_ADC0_IRQ_PRIORITY 1
//#define LPC_DMA_IRQ_PRIORITY 2
//#define LPC_ADC1_IRQ_PRIORITY 3
#define LPC43XX_GPT_TIMER0_IRQ_PRIORITY 2
#define LPC43XX_M0_I2C_I2C0_OR_I2C1_IRQ_PRIORITY 3
#define LPC43XX_PIN_INT4_IRQ_PRIORITY 3
#define LPC_SPI_SSP0_OR_SSP1_IRQ_PRIORITY 3
#define LPC_SDC_SDIO_IRQ_PRIORITY 3
#define LPC_RTC_IRQ_PRIORITY 3
#define LPC43XX_GPT_USE_TIMER0 TRUE
#define LPC43XX_M4TXEVENT_IRQ_PRIORITY 3

View File

@ -0,0 +1,172 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "radio.hpp"
#include "rf_path.hpp"
#include "max2837.hpp"
#include "max5864.hpp"
#include "baseband_cpld.hpp"
#include "baseband_sgpio.hpp"
#include "tuning.hpp"
#include "spi_arbiter.hpp"
#include "hackrf_hal.hpp"
#include "hackrf_gpio.hpp"
using namespace hackrf::one;
#include "spi_pp.hpp"
extern SPI ssp1;
namespace radio {
static constexpr uint32_t ssp1_cpsr = 2;
static constexpr uint32_t ssp_scr(
const float pclk_f,
const uint32_t cpsr,
const float spi_f
) {
return static_cast<uint8_t>(pclk_f / cpsr / spi_f - 1);
}
static constexpr SPIConfig ssp_config_max2837 = {
.end_cb = NULL,
.ssport = gpio_max2837_select.port(),
.sspad = gpio_max2837_select.pad(),
.cr0 =
CR0_CLOCKRATE(ssp_scr(ssp1_pclk_f, ssp1_cpsr, max2837_spi_f))
| CR0_FRFSPI
| CR0_DSS16BIT
,
.cpsr = ssp1_cpsr,
};
static constexpr SPIConfig ssp_config_max5864 = {
.end_cb = NULL,
.ssport = gpio_max5864_select.port(),
.sspad = gpio_max5864_select.pad(),
.cr0 =
CR0_CLOCKRATE(ssp_scr(ssp1_pclk_f, ssp1_cpsr, max5864_spi_f))
| CR0_FRFSPI
| CR0_DSS8BIT
,
.cpsr = ssp1_cpsr,
};
static spi::arbiter::Arbiter ssp1_arbiter(ssp1);
static spi::arbiter::Target ssp1_target_max2837 {
ssp1_arbiter,
ssp_config_max2837
};
static spi::arbiter::Target ssp1_target_max5864 {
ssp1_arbiter,
ssp_config_max5864
};
static rf::path::Path rf_path;
rffc507x::RFFC507x first_if;
static max2837::MAX2837 second_if { ssp1_target_max2837 };
static max5864::MAX5864 baseband_codec { ssp1_target_max5864 };
static baseband::CPLD baseband_cpld;
static baseband::SGPIO baseband_sgpio;
static rf::Direction direction { rf::Direction::Receive };
void init() {
rf_path.init();
first_if.init();
second_if.init();
baseband_codec.init();
baseband_cpld.init();
baseband_sgpio.init();
}
void set_direction(const rf::Direction new_direction) {
/* TODO: Refactor all the various "Direction" enumerations into one. */
/* TODO: Only make changes if direction changes, but beware of clock enabling. */
direction = new_direction;
second_if.set_mode((direction == rf::Direction::Transmit) ? max2837::Mode::Transmit : max2837::Mode::Receive);
rf_path.set_direction(direction);
baseband_codec.set_mode((direction == rf::Direction::Transmit) ? max5864::Mode::Transmit : max5864::Mode::Receive);
baseband_sgpio.configure((direction == rf::Direction::Transmit) ? baseband::Direction::Transmit : baseband::Direction::Receive);
}
bool set_tuning_frequency(const rf::Frequency frequency) {
const auto tuning_config = tuning::config::create(frequency);
if( tuning_config.is_valid() ) {
first_if.disable();
if( tuning_config.first_lo_frequency ) {
first_if.set_frequency(tuning_config.first_lo_frequency);
first_if.enable();
}
const auto result_second_if = second_if.set_frequency(tuning_config.second_lo_frequency);
rf_path.set_band(tuning_config.rf_path_band);
baseband_cpld.set_q_invert(tuning_config.baseband_q_invert);
return result_second_if;
} else {
return false;
}
}
void set_rf_amp(const bool rf_amp) {
rf_path.set_rf_amp(rf_amp);
}
void set_lna_gain(const int_fast8_t db) {
second_if.set_lna_gain(db);
}
void set_vga_gain(const int_fast8_t db) {
second_if.set_vga_gain(db);
}
void set_baseband_filter_bandwidth(const uint32_t bandwidth_minimum) {
second_if.set_lpf_rf_bandwidth(bandwidth_minimum);
}
void set_baseband_decimation_by(const size_t n) {
baseband_cpld.set_decimation_by(n);
}
void streaming_enable() {
baseband_sgpio.streaming_enable();
}
void disable() {
baseband_sgpio.streaming_disable();
baseband_codec.set_mode(max5864::Mode::Shutdown);
second_if.set_mode(max2837::Mode::Shutdown);
first_if.disable();
set_rf_amp(false);
}
} /* namespace radio */

View File

@ -0,0 +1,52 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __RADIO_H__
#define __RADIO_H__
#include "rf_path.hpp"
#include <cstdint>
#include <cstddef>
#include "rffc507x.hpp"
namespace radio {
void init();
void set_direction(const rf::Direction new_direction);
bool set_tuning_frequency(const rf::Frequency frequency);
void set_rf_amp(const bool rf_amp);
void set_lna_gain(const int_fast8_t db);
void set_vga_gain(const int_fast8_t db);
void set_sampling_frequency(const uint32_t frequency);
void set_baseband_filter_bandwidth(const uint32_t bandwidth_minimum);
void set_baseband_decimation_by(const size_t n);
void streaming_enable();
void disable();
extern rffc507x::RFFC507x first_if;
} /* namespace radio */
#endif/*__RADIO_H__*/

View File

@ -0,0 +1,224 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "receiver_model.hpp"
#include "portapack_shared_memory.hpp"
#include "radio.hpp"
#include "wm8731.hpp"
// TODO: Nasty. Put this in an #include somewhere, or a shared system state
// object?
extern wolfson::wm8731::WM8731 audio_codec;
rf::Frequency ReceiverModel::tuning_frequency() const {
return tuning_frequency_;
}
void ReceiverModel::set_tuning_frequency(rf::Frequency f) {
tuning_frequency_ = f;
update_tuning_frequency();
}
rf::Frequency ReceiverModel::frequency_step() const {
return frequency_step_;
}
void ReceiverModel::set_frequency_step(rf::Frequency f) {
frequency_step_ = f;
}
bool ReceiverModel::rf_amp() const {
return rf_amp_;
}
void ReceiverModel::set_rf_amp(bool enabled) {
rf_amp_ = enabled;
update_rf_amp();
}
int32_t ReceiverModel::lna() const {
return lna_gain_db_;
}
void ReceiverModel::set_lna(int32_t v_db) {
lna_gain_db_ = v_db;
update_lna();
}
uint32_t ReceiverModel::baseband_bandwidth() const {
return baseband_bandwidth_;
}
void ReceiverModel::set_baseband_bandwidth(uint32_t v) {
baseband_bandwidth_ = v;
update_baseband_bandwidth();
}
int32_t ReceiverModel::vga() const {
return vga_gain_db_;
}
void ReceiverModel::set_vga(int32_t v_db) {
vga_gain_db_ = v_db;
update_vga();
}
uint32_t ReceiverModel::sampling_rate() const {
return baseband_configuration.sampling_rate;
}
void ReceiverModel::set_sampling_rate(uint32_t hz) {
baseband_configuration.sampling_rate = hz;
update_baseband_configuration();
}
uint32_t ReceiverModel::modulation() const {
return baseband_configuration.mode;
}
void ReceiverModel::set_modulation(uint32_t v) {
baseband_configuration.mode = v;
update_modulation();
}
volume_t ReceiverModel::headphone_volume() const {
return headphone_volume_;
}
void ReceiverModel::set_headphone_volume(volume_t v) {
headphone_volume_ = v;
update_headphone_volume();
}
uint32_t ReceiverModel::baseband_oversampling() const {
// TODO: Rename decimation_factor.
return baseband_configuration.decimation_factor;
}
void ReceiverModel::set_baseband_oversampling(uint32_t v) {
baseband_configuration.decimation_factor = v;
update_baseband_configuration();
}
void ReceiverModel::enable() {
radio::set_direction(rf::Direction::Receive);
update_tuning_frequency();
update_rf_amp();
update_lna();
update_vga();
update_baseband_bandwidth();
update_baseband_configuration();
radio::streaming_enable();
update_headphone_volume();
}
void ReceiverModel::disable() {
/* TODO: This is a dumb hack to stop baseband from working so hard. */
BasebandConfigurationMessage message {
.configuration = {
.mode = -1,
.sampling_rate = 0,
.decimation_factor = 1,
}
};
shared_memory.baseband_queue.push(&message);
while( !message.is_free() );
radio::disable();
}
int32_t ReceiverModel::tuning_offset() {
return -(sampling_rate() / 4);
}
void ReceiverModel::update_tuning_frequency() {
radio::set_tuning_frequency(tuning_frequency_ + tuning_offset());
}
void ReceiverModel::update_rf_amp() {
radio::set_rf_amp(rf_amp_);
}
void ReceiverModel::update_lna() {
radio::set_lna_gain(lna_gain_db_);
}
void ReceiverModel::update_baseband_bandwidth() {
radio::set_baseband_filter_bandwidth(baseband_bandwidth_);
}
void ReceiverModel::update_vga() {
radio::set_vga_gain(vga_gain_db_);
}
void ReceiverModel::update_modulation() {
update_baseband_configuration();
}
void ReceiverModel::update_baseband_configuration() {
clock_manager.set_sampling_frequency(sampling_rate() * baseband_oversampling());
update_tuning_frequency();
radio::set_baseband_decimation_by(baseband_oversampling());
BasebandConfigurationMessage message { baseband_configuration };
shared_memory.baseband_queue.push(&message);
// Block until message is consumed, since we allocated it on the stack.
while( !message.is_free() );
if( baseband_configuration.mode == 3 ) {
update_fsk_configuration();
}
}
void ReceiverModel::update_headphone_volume() {
// TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do
// both?
audio_codec.set_headphone_volume(headphone_volume_);
}
static constexpr FSKConfiguration fsk_configuration_ais = {
.symbol_rate = 9600,
.access_code = 0b01010101010101010101111110,
.access_code_length = 26,
.access_code_tolerance = 1,
.packet_length = 256,
};
static constexpr FSKConfiguration fsk_configuration_tpms_a = {
.symbol_rate = 19200,
.access_code = 0b0101010101010101010101010110,
.access_code_length = 28,
.access_code_tolerance = 1,
.packet_length = 160,
};
void ReceiverModel::update_fsk_configuration() {
FSKConfigurationMessage message { fsk_configuration_ais };
shared_memory.baseband_queue.push(&message);
// Block until message is consumed, since we allocated it on the stack.
while( !message.is_free() );
}

View File

@ -0,0 +1,111 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __RECEIVER_MODEL_H__
#define __RECEIVER_MODEL_H__
#include <cstdint>
#include <cstddef>
#include "clock_manager.hpp"
#include "message.hpp"
#include "rf_path.hpp"
#include "max2837.hpp"
#include "volume.hpp"
class ReceiverModel {
public:
constexpr ReceiverModel(
ClockManager& clock_manager
) : clock_manager { clock_manager }
{
}
rf::Frequency tuning_frequency() const;
void set_tuning_frequency(rf::Frequency f);
rf::Frequency frequency_step() const;
void set_frequency_step(rf::Frequency f);
bool rf_amp() const;
void set_rf_amp(bool enabled);
int32_t lna() const;
void set_lna(int32_t v_db);
uint32_t baseband_bandwidth() const;
void set_baseband_bandwidth(uint32_t v);
int32_t vga() const;
void set_vga(int32_t v_db);
uint32_t sampling_rate() const;
void set_sampling_rate(uint32_t hz);
uint32_t modulation() const;
void set_modulation(uint32_t v);
volume_t headphone_volume() const;
void set_headphone_volume(volume_t v);
uint32_t baseband_oversampling() const;
void set_baseband_oversampling(uint32_t v);
void enable();
void disable();
private:
rf::Frequency tuning_frequency_ {
//118099000
//128349000
//162523000
//162548000
//858450000
858752000
};
rf::Frequency frequency_step_ { 25000 };
bool rf_amp_ { true };
int32_t lna_gain_db_ { 32 };
uint32_t baseband_bandwidth_ { max2837::filter::bandwidth_minimum };
int32_t vga_gain_db_ { 32 };
BasebandConfiguration baseband_configuration {
.mode = 1, /* TODO: Enum! */
.sampling_rate = 3072000,
.decimation_factor = 4,
};
volume_t headphone_volume_ { -43.0_dB };
ClockManager& clock_manager;
int32_t tuning_offset();
void update_tuning_frequency();
void update_rf_amp();
void update_lna();
void update_baseband_bandwidth();
void update_vga();
void update_modulation();
void update_baseband_configuration();
void update_headphone_volume();
void update_fsk_configuration();
};
#endif/*__RECEIVER_MODEL_H__*/

View File

@ -0,0 +1,224 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "rf_path.hpp"
#include <array>
#include <initializer_list>
#include "hackrf_gpio.hpp"
using namespace hackrf::one;
#include "utility.hpp"
namespace rf {
namespace path {
namespace {
using GPIOs = std::array<GPIO, 13>;
/* TODO: ARM GCC 4.8 2014q3 doesn't like this array inside struct Config.
* No idea why.
*/
constexpr GPIOs gpios {
gpio_tx,
gpio_rx,
gpio_mix_bypass,
gpio_not_mix_bypass,
gpio_tx_mix_bp,
gpio_rx_mix_bp,
gpio_hp,
gpio_lp,
gpio_amp_bypass,
gpio_tx_amp,
gpio_not_tx_amp_pwr,
gpio_rx_amp,
gpio_not_rx_amp_pwr,
};
struct Config {
using base_type = uint16_t;
union {
struct {
bool tx : 1;
bool rx : 1;
bool mix_bypass : 1;
bool not_mix_bypass : 1;
bool tx_mix_bp : 1;
bool rx_mix_bp : 1;
bool hp : 1;
bool lp : 1;
bool amp_bypass : 1;
bool tx_amp : 1;
bool not_tx_amp : 1;
bool rx_amp : 1;
bool not_rx_amp : 1;
};
base_type w;
};
constexpr Config(
const Direction direction,
const Band band,
const bool amplify
) :
tx(direction == Direction::Transmit),
rx(direction == Direction::Receive),
mix_bypass(band == Band::Mid),
not_mix_bypass(band != Band::Mid),
tx_mix_bp((direction == Direction::Transmit) && (band == Band::Mid)),
rx_mix_bp((direction == Direction::Receive) && (band == Band::Mid)),
hp(band == Band::High),
lp(band == Band::Low),
amp_bypass(!amplify),
tx_amp((direction == Direction::Transmit) && amplify),
not_tx_amp(!((direction == Direction::Transmit) && amplify)),
rx_amp((direction == Direction::Receive) && amplify),
not_rx_amp(!((direction == Direction::Receive) && amplify))
{
}
constexpr Config(
) : Config(Direction::Receive, Band::Mid, false)
{
}
constexpr Config(
const base_type w
) : w(w)
{
}
constexpr Config operator^(const Config& r) const {
return w ^ r.w;
}
constexpr Config operator&(const Config& r) const {
return w & r.w;
}
constexpr bool operator[](const size_t n) const {
return (w >> n) & 1;
}
static void gpio_init() {
for(auto gpio : gpios) {
gpio.output();
}
}
void apply() const {
/* NOTE: Assumes order in gpios[] and Config bitfield match. */
for(size_t n=0; n<gpios.size(); n++) {
gpios[n].write((*this)[n]);
}
}
};
using ConfigAmp = std::array<Config, 2>;
using ConfigDirection = std::array<ConfigAmp, 2>;
using ConfigBand = std::array<ConfigDirection, 3>;
constexpr ConfigAmp config_amp(
const Direction direction,
const Band band
) {
return { {
{ .direction = direction, .band = band, .amplify = false },
{ .direction = direction, .band = band, .amplify = true },
} };
}
constexpr ConfigDirection config_rx_tx(
const Band band
) {
return {
config_amp(Direction::Receive, band),
config_amp(Direction::Transmit, band),
};
}
constexpr ConfigBand config_band() {
return {
config_rx_tx(Band::Low),
config_rx_tx(Band::Mid),
config_rx_tx(Band::High),
};
}
constexpr ConfigBand config_table = config_band();
static_assert(sizeof(config_table) == sizeof(Config::base_type) * 3 * 2 * 2, "rf path config table unexpected size");
constexpr Config get_config(
const Direction direction,
const Band band,
const bool amplify
) {
return config_table[toUType(band)][toUType(direction)][amplify ? 1 : 0];
}
} /* namespace */
void Path::init() {
update();
Config::gpio_init();
}
void Path::set_direction(const Direction new_direction) {
direction = new_direction;
update();
}
void Path::set_band(const Band new_band) {
band = new_band;
update();
}
void Path::set_rf_amp(const bool new_rf_amp) {
rf_amp = new_rf_amp;
update();
}
void Path::update() {
/* 0 ^ 0 => 0 & 0 = 0 ^ 0 = 0 (no change)
* 0 ^ 1 => 1 & 0 = 0 ^ 0 = 0 (ignore change to 1)
* 1 ^ 0 => 1 & 1 = 1 ^ 1 = 0 (allow change to 0)
* 1 ^ 1 => 0 & 1 = 0 ^ 1 = 1 (no change)
*/
//const Config changed = _config ^ config_next;
//const Config turned_off = _config & changed;
/* In transition, ignore the bits that are turning on. So this transition phase
* only turns off signals. It doesn't turn on signals.
*/
//const Config transition_config = _config ^ turned_off;
//update_signals(transition_config);
/* Move to the final state by turning on required signals. */
const auto config = get_config(direction, band, rf_amp);
config.apply();
}
} /* path */
} /* rf */

View File

@ -0,0 +1,104 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __RF_PATH_H__
#define __RF_PATH_H__
#include <cstdint>
namespace rf {
using Frequency = int64_t;
struct FrequencyRange {
Frequency min;
Frequency max;
/* TODO: static_assert low < high? */
bool below_range(const Frequency f) const {
return f < min;
}
bool contains(const Frequency f) const {
return (f >= min) && (f < max);
}
bool out_of_range(const Frequency f) const {
return !contains(f);
}
};
enum class Direction {
/* Zero-based, used as index into table */
Receive = 0,
Transmit = 1,
};
namespace path {
constexpr FrequencyRange band_low {
.min = 0,
.max = 2150000000,
};
constexpr FrequencyRange band_high {
.min = 2750000000,
.max = 7250000000,
};
constexpr FrequencyRange band_mid {
.min = band_low.max,
.max = band_high.min,
};
enum class Band {
/* Zero-based, used as index into frequency_bands table */
Low = 0,
Mid = 1,
High = 2,
};
class Path {
public:
void init();
void set_direction(const Direction direction);
void set_band(const Band band);
void set_rf_amp(const bool rf_amp);
private:
Direction direction { Direction::Receive };
Band band { Band::Mid };
bool rf_amp { false };
void update();
};
} /* path */
constexpr FrequencyRange tuning_range {
.min = path::band_low.min,
.max = path::band_high.max,
};
} /* rf */
#endif/*__RF_PATH_H__*/

View File

@ -0,0 +1,296 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "rffc507x.hpp"
#include <array>
#include "utility.hpp"
#include "hackrf_hal.hpp"
#include "hackrf_gpio.hpp"
using namespace hackrf::one;
#include "hal.h"
namespace rffc507x {
/* Empirical tests indicate no minimum reset pulse width, but the speed
* of the processor and GPIO probably produce at least 20ns pulse width.
*/
constexpr float seconds_during_reset = 1.0e-6;
constexpr halrtcnt_t ticks_during_reset = (base_m4_clk_f * seconds_during_reset + 1);
/* Empirical testing indicates >3.5us delay required after reset, before
* registers can be reliably written. Make it 5us, just for fun. Tests were
* conducted at high temperatures (with a hair dryer) increased room
* temperature minimum delay of 2.9us to the requirement above.
*/
constexpr float seconds_after_reset = 5.0e-6;
constexpr halrtcnt_t ticks_after_reset = (base_m4_clk_f * seconds_after_reset + 1);
constexpr auto reference_frequency = rffc5072_reference_f;
namespace vco {
constexpr rf::FrequencyRange range {
.min = 2700000000U,
.max = 5400000000U,
};
} /* namespace vco */
namespace lo {
constexpr size_t divider_log2_min = 0;
constexpr size_t divider_log2_max = 5;
constexpr size_t divider_min = 1U << divider_log2_min;
constexpr size_t divider_max = 1U << divider_log2_max;
constexpr rf::FrequencyRange range {
.min = vco::range.min / divider_max,
.max = vco::range.max / divider_min,
};
size_t divider_log2(const rf::Frequency lo_frequency) {
/* TODO: Error */
/*
if( lo::range.out_of_range(lo_frequency) ) {
return;
}
*/
/* Compute LO divider. */
auto lo_divider_log2 = lo::divider_log2_min;
auto vco_frequency = lo_frequency;
while( vco::range.below_range(vco_frequency) ) {
vco_frequency <<= 1;
lo_divider_log2 += 1;
}
return lo_divider_log2;
}
} /* namespace lo */
namespace prescaler {
constexpr rf::Frequency max_frequency = 1600000000U;
constexpr size_t divider_log2_min = 1;
constexpr size_t divider_log2_max = 2;
constexpr size_t divider_min = 1U << divider_log2_min;
constexpr size_t divider_max = 1U << divider_log2_max;
constexpr size_t divider_log2(const rf::Frequency vco_frequency) {
return (vco_frequency > (prescaler::divider_min * prescaler::max_frequency))
? prescaler::divider_log2_max
: prescaler::divider_log2_min
;
}
} /* namespace prescaler */
struct SynthConfig {
const size_t lo_divider_log2;
const size_t prescaler_divider_log2;
const uint64_t n_divider_q24;
static SynthConfig calculate(
const rf::Frequency lo_frequency
) {
/* RFFC507x frequency synthesizer is is accurate to about 2ppb (two parts
* per BILLION). There's not much point to worrying about rounding and
* tuning error, when it amounts to 8Hz at 5GHz!
*/
const size_t lo_divider_log2 = lo::divider_log2(lo_frequency);
const size_t lo_divider = 1U << lo_divider_log2;
const rf::Frequency vco_frequency = lo_frequency * lo_divider;
const size_t prescaler_divider_log2 = prescaler::divider_log2(vco_frequency);
const uint64_t prescaled_lo_q24 = vco_frequency << (24 - prescaler_divider_log2);
const uint64_t n_divider_q24 = prescaled_lo_q24 / reference_frequency;
return {
lo_divider_log2,
prescaler_divider_log2,
n_divider_q24,
};
}
};
/* Readback values, RFFC5072 rev A:
* 0000: 0x8a01 => dev_id=1000101000000 mrev_id=001
* 0001: 0x3f7c => lock=0 ct_cal=0111111 cp_cal=011111 ctfail=0 0
* 0010: 0x806f => v0_cal=10000000 v1_cal=01101111
* 0011: 0x0000 => rsm_state=00000 f_errflag=00
* 0100: 0x0000 => vco_count_l=0
* 0101: 0x0000 => vco_count_h=0
* 0110: 0xc000 => cal_fbi=1 cal_fbq=1
* 0111: 0x0000 => vco_sel=0 vco_tc_curve=0
*/
void RFFC507x::init() {
gpio_rffc5072_resetx.set();
gpio_rffc5072_resetx.output();
reset();
_bus.init();
_dirty.set();
flush();
}
void RFFC507x::reset() {
/* TODO: Is RESETB pin ignored if sdi_ctrl.sipin=1? Programming guide
* description of sdi_ctrl.sipin suggests the pin is not ignored.
*/
gpio_rffc5072_resetx.clear();
halPolledDelay(ticks_during_reset);
gpio_rffc5072_resetx.set();
halPolledDelay(ticks_after_reset);
}
void RFFC507x::flush() {
if( _dirty ) {
for(size_t i=0; i<_map.w.size(); i++) {
if( _dirty[i] ) {
write(i, _map.w[i]);
}
}
_dirty.clear();
}
}
inline void RFFC507x::write(const address_t reg_num, const spi::reg_t value) {
_bus.write(reg_num, value);
}
inline spi::reg_t RFFC507x::read(const address_t reg_num) {
return _bus.read(reg_num);
}
inline void RFFC507x::write(const Register reg, const spi::reg_t value) {
write(toUType(reg), value);
}
inline spi::reg_t RFFC507x::read(const Register reg) {
return read(toUType(reg));
}
inline void RFFC507x::flush_one(const Register reg) {
const auto reg_num = toUType(reg);
write(reg_num, _map.w[reg_num]);
_dirty.clear(reg_num);
}
void RFFC507x::enable() {
_map.r.sdi_ctrl.enbl = 1;
flush_one(Register::SDI_CTRL);
/* TODO: Reset PLLCPL after CT_CAL? */
/* TODO: After device is enabled and CT_cal is complete and VCO > 3.2GHz,
* change prescaler divider to 2, update synthesizer ratio, change
* lf.pllcpl from 3 to 2.
*/
}
void RFFC507x::disable() {
_map.r.sdi_ctrl.enbl = 0;
flush_one(Register::SDI_CTRL);
}
void RFFC507x::set_mixer_current(const uint8_t value) {
/* MIX IDD = 0b000 appears to turn the mixer completely off */
/* TODO: Adjust mixer current. Graphs in datasheet suggest:
* MIX_IDD=1 has lowest noise figure (10.1dB vs 13dB @ MIX_IDD=7).
* MIX_IDD=5 has highest IP3 (24dBm vs 10.3dBm @ MIX_IDD=1).
* MIX_IDD=5 has highest P1dB (11.8dBm vs 1.5dBm @ MIX_IDD=1).
* Mixer input impedance ~85 Ohms at MIX_IDD=4.
* Mixer input impedance inversely proportional to MIX_IDD.
* Balun balanced (mixer) side is 100 Ohms. Perhaps reduce MIX_IDD
* a bit to get 100 Ohms from mixer.
*/
_map.r.mix_cont.p1mixidd = value;
_map.r.mix_cont.p2mixidd = value;
flush_one(Register::MIX_CONT);
}
void RFFC507x::set_frequency(const rf::Frequency lo_frequency) {
const SynthConfig synth_config = SynthConfig::calculate(lo_frequency);
/* Boost charge pump leakage if VCO frequency > 3.2GHz, indicated by
* prescaler divider set to 4 (log2=2) instead of 2 (log2=1).
*/
if( synth_config.prescaler_divider_log2 == 2 ) {
_map.r.lf.pllcpl = 3;
} else {
_map.r.lf.pllcpl = 2;
}
flush_one(Register::LF);
_map.r.p2_freq1.p2n = synth_config.n_divider_q24 >> 24;
_map.r.p2_freq1.p2lodiv = synth_config.lo_divider_log2;
_map.r.p2_freq1.p2presc = synth_config.prescaler_divider_log2;
_map.r.p2_freq2.p2nmsb = (synth_config.n_divider_q24 >> 8) & 0xffff;
_map.r.p2_freq3.p2nlsb = synth_config.n_divider_q24 & 0xff;
_dirty[Register::P2_FREQ1] = 1;
_dirty[Register::P2_FREQ2] = 1;
_dirty[Register::P2_FREQ3] = 1;
flush();
}
spi::reg_t RFFC507x::readback(const Readback readback) {
/* TODO: This clobbers the rest of the DEV_CTRL register
* Time to implement bitfields for registers.
*/
_map.r.dev_ctrl.readsel = toUType(readback);
flush_one(Register::DEV_CTRL);
return read(Register::READBACK);
}
RegisterMap RFFC507x::registers() {
return _map;
}
#if 0
/* Test of RFFC507x reset over temperature */
while(true) {
first_if.write(rffc507x::Register::P1_FREQ2, 0xAAAA);
first_if.reset();
const auto after_reset = first_if.read(rffc507x::Register::P1_FREQ2);
if( after_reset != 0x6276 ) {
led_usb.off();
led_tx.on();
chThdSleepMilliseconds(100);
} else {
led_usb.on();
led_tx.off();
}
}
#endif
} /* namespace rffc507x */

View File

@ -0,0 +1,835 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __RFFC507X_H__
#define __RFFC507X_H__
#include "rffc507x_spi.hpp"
#include <cstdint>
#include <array>
#include "dirty_registers.hpp"
#include "rf_path.hpp"
namespace rffc507x {
using reg_t = spi::reg_t;
using address_t = spi::address_t;
constexpr size_t reg_count = 31;
enum class Register : address_t {
LF = 0x00,
XO = 0x01,
CAL_TIME = 0x02,
VCO_CTRL = 0x03,
CT_CAL1 = 0x04,
CT_CAL2 = 0x05,
PLL_CAL1 = 0x06,
PLL_CAL2 = 0x07,
VCO_AUTO = 0x08,
PLL_CTRL = 0x09,
PLL_BIAS = 0x0a,
MIX_CONT = 0x0b,
P1_FREQ1 = 0x0c,
P1_FREQ2 = 0x0d,
P1_FREQ3 = 0x0e,
P2_FREQ1 = 0x0f,
P2_FREQ2 = 0x10,
P2_FREQ3 = 0x11,
FN_CTRL = 0x12,
EXT_MOD = 0x13,
FMOD = 0x14,
SDI_CTRL = 0x15,
GPO = 0x16,
T_VCO = 0x17,
IQMOD1 = 0x18,
IQMOD2 = 0x19,
IQMOD3 = 0x1a,
IQMOD4 = 0x1b,
T_CTRL = 0x1c,
DEV_CTRL = 0x1d,
TEST = 0x1e,
READBACK = 0x1f,
};
enum class Readback : uint8_t {
DeviceID = 0b0000,
TuningCalibration = 0b0001,
TuningVoltage = 0b0010,
StateMachine = 0b0011,
VCOCountL = 0b0100,
VCOCountH = 0b0101,
DCOffsetCal = 0b0110,
VCOMode = 0b0111,
};
struct LF_Type {
reg_t pllcpl : 3;
reg_t p1cpdef : 6;
reg_t p2cpdef : 6;
reg_t lfact : 1;
};
static_assert(sizeof(LF_Type) == sizeof(reg_t), "LF_Type type wrong size");
struct XO_Type {
reg_t suwait : 10;
reg_t xocf : 1;
reg_t xoc : 4;
reg_t xoch : 1;
};
static_assert(sizeof(XO_Type) == sizeof(reg_t), "XO_Type type wrong size");
struct CAL_TIME_Type {
reg_t tkv2 : 4;
reg_t tkv1 : 4;
reg_t reserved0 : 2;
reg_t tct : 5;
reg_t wait : 1;
};
static_assert(sizeof(CAL_TIME_Type) == sizeof(reg_t), "CAL_TIME_Type type wrong size");
struct VCO_CTRL_Type {
reg_t reserved0 : 1;
reg_t icpup : 2;
reg_t refst : 1;
reg_t xoi3 : 1;
reg_t xoi2 : 1;
reg_t xoi1 : 1;
reg_t kvpol : 1;
reg_t kvrng : 1;
reg_t kvavg : 2;
reg_t clkpl : 1;
reg_t ctpol : 1;
reg_t ctavg : 2;
reg_t xtvco : 1;
};
static_assert(sizeof(VCO_CTRL_Type) == sizeof(reg_t), "VCO_CTRL_Type type wrong size");
struct CT_CAL1_Type {
reg_t p1ctdef : 7;
reg_t p1ct : 1;
reg_t p1ctv : 5;
reg_t p1ctgain : 3;
};
static_assert(sizeof(CT_CAL1_Type) == sizeof(reg_t), "CT_CAL1_Type type wrong size");
struct CT_CAL2_Type {
reg_t p2ctdef : 7;
reg_t p2ct : 1;
reg_t p2ctv : 5;
reg_t p2ctgain : 3;
};
static_assert(sizeof(CT_CAL2_Type) == sizeof(reg_t), "CT_CAL2_Type type wrong size");
struct PLL_CAL1_Type {
reg_t reserved0 : 2;
reg_t p1sgn : 1;
reg_t p1kvgain : 3;
reg_t p1dn : 9;
reg_t p1kv : 1;
};
static_assert(sizeof(PLL_CAL1_Type) == sizeof(reg_t), "PLL_CAL1_Type type wrong size");
struct PLL_CAL2_Type {
reg_t reserved0 : 2;
reg_t p2sgn : 1;
reg_t p2kvgain : 3;
reg_t p2dn : 9;
reg_t p2kv : 1;
};
static_assert(sizeof(PLL_CAL2_Type) == sizeof(reg_t), "PLL_CAL2_Type type wrong size");
struct VCO_AUTO_Type {
reg_t reserved0 : 1;
reg_t ctmin : 7;
reg_t ctmax : 7;
reg_t auto_ : 1;
};
static_assert(sizeof(VCO_AUTO_Type) == sizeof(reg_t), "VCO_AUTO_Type type wrong size");
struct PLL_CTRL_Type {
reg_t plldy : 2;
reg_t aloi : 1;
reg_t relok : 1;
reg_t ldlev : 1;
reg_t lden : 1;
reg_t tvco : 5;
reg_t pllst : 1;
reg_t clkdiv : 3;
reg_t divby : 1;
};
static_assert(sizeof(PLL_CTRL_Type) == sizeof(reg_t), "PLL_CTRL_Type type wrong size");
struct PLL_BIAS_Type {
reg_t p2vcoi : 3;
reg_t p2loi : 4;
reg_t reserved0 : 1;
reg_t p1vcoi : 3;
reg_t p1loi : 4;
reg_t reserved1 : 1;
};
static_assert(sizeof(PLL_BIAS_Type) == sizeof(reg_t), "PLL_BIAS_Type type wrong size");
struct MIX_CONT_Type {
reg_t reserved0 : 9;
reg_t p2mixidd : 3;
reg_t p1mixidd : 3;
reg_t fulld : 1;
};
static_assert(sizeof(MIX_CONT_Type) == sizeof(reg_t), "MIX_CONT_Type type wrong size");
struct P1_FREQ1_Type {
reg_t p1vcosel : 2;
reg_t p1presc : 2;
reg_t p1lodiv : 3;
reg_t p1n : 9;
};
static_assert(sizeof(P1_FREQ1_Type) == sizeof(reg_t), "P1_FREQ1_Type type wrong size");
struct P1_FREQ2_Type {
reg_t p1nmsb : 16;
};
static_assert(sizeof(P1_FREQ2_Type) == sizeof(reg_t), "P1_FREQ2_Type type wrong size");
struct P1_FREQ3_Type {
reg_t reserved0 : 8;
reg_t p1nlsb : 8;
};
static_assert(sizeof(P1_FREQ3_Type) == sizeof(reg_t), "P1_FREQ3_Type type wrong size");
struct P2_FREQ1_Type {
reg_t p2vcosel : 2;
reg_t p2presc : 2;
reg_t p2lodiv : 3;
reg_t p2n : 9;
};
static_assert(sizeof(P2_FREQ1_Type) == sizeof(reg_t), "P2_FREQ1_Type type wrong size");
struct P2_FREQ2_Type {
reg_t p2nmsb : 16;
};
static_assert(sizeof(P2_FREQ2_Type) == sizeof(reg_t), "P2_FREQ2_Type type wrong size");
struct P2_FREQ3_Type {
reg_t reserved0 : 8;
reg_t p2nlsb : 8;
};
static_assert(sizeof(P2_FREQ3_Type) == sizeof(reg_t), "P2_FREQ3_Type type wrong size");
struct FN_CTRL_Type {
reg_t reserved0 : 1;
reg_t tzps : 1;
reg_t dmode : 1;
reg_t fm : 1;
reg_t dith : 1;
reg_t mode : 1;
reg_t phsalndly : 2;
reg_t phsalngain : 3;
reg_t phaln : 1;
reg_t sdm : 2;
reg_t dithr : 1;
reg_t fnz : 1;
};
static_assert(sizeof(FN_CTRL_Type) == sizeof(reg_t), "FN_CTRL_Type type wrong size");
struct EXT_MOD_Type {
reg_t reserved0 : 10;
reg_t modstep : 4;
reg_t modsetup : 2;
};
static_assert(sizeof(EXT_MOD_Type) == sizeof(reg_t), "EXT_MOD_Type type wrong size");
struct FMOD_Type {
reg_t modulation : 16;
};
static_assert(sizeof(FMOD_Type) == sizeof(reg_t), "FMOD_Type type wrong size");
struct SDI_CTRL_Type {
reg_t reserved0 : 1;
reg_t reset : 1;
reg_t reserved1 : 9;
reg_t addr : 1;
reg_t fourwire : 1;
reg_t mode : 1;
reg_t enbl : 1;
reg_t sipin : 1;
};
static_assert(sizeof(SDI_CTRL_Type) == sizeof(reg_t), "SDI_CTRL_Type type wrong size");
struct GPO_Type {
reg_t lock : 1;
reg_t gate : 1;
reg_t p1gpo : 7;
reg_t p2gpo : 7;
};
static_assert(sizeof(GPO_Type) == sizeof(reg_t), "GPO_Type type wrong size");
struct T_VCO_Type {
reg_t reserved0 : 7;
reg_t curve_vco3 : 3;
reg_t curve_vco2 : 3;
reg_t curve_vco1 : 3;
};
static_assert(sizeof(T_VCO_Type) == sizeof(reg_t), "T_VCO_Type type wrong size");
struct IQMOD1_Type {
reg_t bufdc : 2;
reg_t divbias : 1;
reg_t calblk : 1;
reg_t calnul : 1;
reg_t calon : 1;
reg_t lobias : 2;
reg_t modbias : 3;
/* Also defined as ctrl : 5 */
reg_t modiv : 1;
reg_t mod : 1;
reg_t txlo : 1;
reg_t bbgm : 1;
reg_t ctrl : 1;
};
static_assert(sizeof(IQMOD1_Type) == sizeof(reg_t), "IQMOD1_Type type wrong size");
struct IQMOD2_Type {
reg_t modbuf : 2;
reg_t mod : 2;
reg_t calatten : 2;
reg_t rctune : 6;
reg_t bbatten : 4;
};
static_assert(sizeof(IQMOD2_Type) == sizeof(reg_t), "IQMOD2_Type type wrong size");
struct IQMOD3_Type {
/* Documentation error */
reg_t reserved0 : 3;
reg_t dacen : 1;
reg_t bufdacq : 6;
reg_t bufdaci : 6;
};
static_assert(sizeof(IQMOD3_Type) == sizeof(reg_t), "IQMOD3_Type type wrong size");
struct IQMOD4_Type {
/* Documentation error */
reg_t bufbias2 : 2;
reg_t bufbias1 : 2;
reg_t moddacq : 6;
reg_t moddaci : 6;
};
static_assert(sizeof(IQMOD4_Type) == sizeof(reg_t), "IQMOD4_Type type wrong size");
struct T_CTRL_Type {
reg_t reserved0 : 5;
reg_t v_test : 1;
reg_t ldo_by : 1;
reg_t ext_filt : 1;
reg_t ref_sel : 1;
reg_t filt_ctrl : 2;
reg_t fc_en : 1;
reg_t tbl_sel : 2;
reg_t tc_en : 2;
};
static_assert(sizeof(T_CTRL_Type) == sizeof(reg_t), "T_CTRL_Type type wrong size");
struct DEV_CTRL_Type {
reg_t reserved0 : 1;
reg_t bypas : 1;
reg_t ctclk : 1;
reg_t dac : 1;
reg_t cpd : 1;
reg_t cpu : 1;
reg_t rsmstopst : 5;
reg_t rsmst : 1;
reg_t readsel : 4;
};
static_assert(sizeof(DEV_CTRL_Type) == sizeof(reg_t), "DEV_CTRL_Type type wrong size");
struct TEST_Type {
reg_t lfsrd : 1;
reg_t rcbyp : 1;
reg_t rgbyp : 1;
reg_t lfsrt : 1;
reg_t lfsrgatetime : 4;
reg_t lfsrp : 1;
reg_t lfsr : 1;
reg_t tsel : 2;
reg_t tmux : 3;
reg_t ten : 1;
};
static_assert(sizeof(TEST_Type) == sizeof(reg_t), "TEST_Type type wrong size");
struct READBACK_0000_Type {
reg_t mrev_id : 3;
reg_t dev_id : 13;
};
static_assert(sizeof(READBACK_0000_Type) == sizeof(reg_t), "READBACK_0000_Type type wrong size");
struct READBACK_0001_Type {
reg_t reserved0 : 1;
reg_t ctfail : 1;
reg_t cp_cal : 6;
reg_t ct_cal : 7;
reg_t lock : 1;
};
static_assert(sizeof(READBACK_0001_Type) == sizeof(reg_t), "READBACK_0001_Type type wrong size");
struct READBACK_0010_Type {
reg_t v1_cal : 8;
reg_t v0_cal : 8;
};
static_assert(sizeof(READBACK_0010_Type) == sizeof(reg_t), "READBACK_0010_Type type wrong size");
struct READBACK_0011_Type {
reg_t reserved0 : 9;
reg_t f_errflag : 2;
reg_t rsm_state : 5;
};
static_assert(sizeof(READBACK_0011_Type) == sizeof(reg_t), "READBACK_0011_Type type wrong size");
struct READBACK_0100_Type {
reg_t vco_count_l : 16;
};
static_assert(sizeof(READBACK_0100_Type) == sizeof(reg_t), "READBACK_0100_Type type wrong size");
struct READBACK_0101_Type {
reg_t vco_count_h : 16;
};
static_assert(sizeof(READBACK_0101_Type) == sizeof(reg_t), "READBACK_0101_Type type wrong size");
struct READBACK_0110_Type {
reg_t reserved0 : 14;
reg_t cal_fbq : 1;
reg_t cal_fbi : 1;
};
static_assert(sizeof(READBACK_0110_Type) == sizeof(reg_t), "READBACK_0110_Type type wrong size");
struct READBACK_0111_Type {
reg_t reserved0 : 11;
reg_t vco_tc_curve : 3;
reg_t vco_sel : 2;
};
static_assert(sizeof(READBACK_0111_Type) == sizeof(reg_t), "READBACK_0111_Type type wrong size");
struct Register_Type {
LF_Type lf;
XO_Type xo;
CAL_TIME_Type cal_time;
VCO_CTRL_Type vco_ctrl;
CT_CAL1_Type ct_cal1;
CT_CAL2_Type ct_cal2;
PLL_CAL1_Type pll_cal1;
PLL_CAL2_Type pll_cal2;
VCO_AUTO_Type vco_auto;
PLL_CTRL_Type pll_ctrl;
PLL_BIAS_Type pll_bias;
MIX_CONT_Type mix_cont;
P1_FREQ1_Type p1_freq1;
P1_FREQ2_Type p1_freq2;
P1_FREQ3_Type p1_freq3;
P2_FREQ1_Type p2_freq1;
P2_FREQ2_Type p2_freq2;
P2_FREQ3_Type p2_freq3;
FN_CTRL_Type fn_ctrl;
EXT_MOD_Type ext_mod;
FMOD_Type fmod;
SDI_CTRL_Type sdi_ctrl;
GPO_Type gpo;
T_VCO_Type t_vco;
IQMOD1_Type iqmod1;
IQMOD2_Type iqmod2;
IQMOD3_Type iqmod3;
IQMOD4_Type iqmod4;
T_CTRL_Type t_ctrl;
DEV_CTRL_Type dev_ctrl;
TEST_Type test;
};
static_assert(sizeof(Register_Type) == reg_count * sizeof(reg_t), "Register_Type wrong size");
struct RegisterMap {
constexpr RegisterMap(
Register_Type values
) : r(values)
{
}
union {
Register_Type r;
std::array<reg_t, reg_count> w;
};
};
static_assert(sizeof(RegisterMap) == reg_count * sizeof(reg_t), "RegisterMap type wrong size");
#if 0
struct ReadbackType {
union {
READBACK_0000_Type readback_0000;
READBACK_0001_Type readback_0001;
READBACK_0010_Type readback_0010;
READBACK_0011_Type readback_0011;
READBACK_0100_Type readback_0100;
READBACK_0101_Type readback_0101;
READBACK_0110_Type readback_0110;
READBACK_0111_Type readback_0111;
reg_t w;
};
};
#endif
#if 0
/* Revision 1 devices (mrev_id = 001):
* RFFC2071/2072/5071/5072, RFMD2080/2081
*/
constexpr RegisterMap default_revision_1 { std::array<reg_t, reg_count> {
0xbefa, 0x4064, 0x9055, 0x2d02,
0xb0bf, 0xb0bf, 0x0028, 0x0028,
0xfc06, 0x8220, 0x0202, 0x4800,
0x2324, 0x6276, 0x2700, 0x2f16,
0x3b13, 0xb100, 0x2a80, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000,
0x0283, 0xf00f, 0x0000, 0x000f,
0x0000, 0x1000, 0x0001,
} };
/* Revision 2 devices (mrev_id = 010):
* RFFC2071A/2072A/5071A/5072A
*/
constexpr RegisterMap default_revision_2 { std::array<reg_t, reg_count> {
0xbefa, 0x4064, 0x9055, 0x2d02,
0xacbf, 0xacbf, 0x0028, 0x0028,
0xff00, 0x8220, 0x0202, 0x4800,
0x1a94, 0xd89d, 0x8900, 0x1e84,
0x89d8, 0x9d00, 0x2a80, 0x0000,
0x0000, 0x0000, 0x0000, 0x4900,
0x0281, 0xf00f, 0x0000, 0x0005,
0xc840, 0x1000, 0x0005,
} };
#endif
constexpr RegisterMap default_hackrf_one { Register_Type {
/* Started with recommended defaults for revision 1 devices
* (mrev_id = 001), RFFC2071/2072/5071/5072, RFMD2080/2081.
* Modified according to mixer programming guide.
*/
.lf = { /* 0 */
.pllcpl = 0b010,
.p1cpdef = 0b011111,
.p2cpdef = 0b011111,
.lfact = 1,
},
.xo = { /* 1 */
.suwait = 0b0001100100,
.xocf = 0b0,
.xoc = 0b1000,
.xoch = 0b0,
},
.cal_time = { /* 2 */
.tkv2 = 0b0101,
.tkv1 = 0b0101,
.reserved0 = 0b00,
.tct = 0b00100,
.wait = 0b1,
},
.vco_ctrl = { /* 3 */
.reserved0 = 0b0,
.icpup = 0b01,
.refst = 0b0,
.xoi3 = 0b0,
.xoi2 = 0b0,
.xoi1 = 0b0,
.kvpol = 0b0,
.kvrng = 0b1,
.kvavg = 0b10,
.clkpl = 0b1,
.ctpol = 0b0,
.ctavg = 0b01,
.xtvco = 0b0,
},
.ct_cal1 = { /* 4 */
.p1ctdef = 0b0111111,
.p1ct = 0b1,
.p1ctv = 12, /* RFMD recommneded change: 16 -> 12 */
.p1ctgain = 0b101,
},
.ct_cal2 = { /* 5 */
.p2ctdef = 0b0111111,
.p2ct = 0b1,
.p2ctv = 12, /* RFMD recommneded change: 16 -> 12 */
.p2ctgain = 0b101,
},
.pll_cal1 = { /* 6 */
.reserved0 = 0b00,
.p1sgn = 0b0,
.p1kvgain = 0b101,
.p1dn = 0b000000000,
.p1kv = 0b0,
},
.pll_cal2 = { /* 7 */
.reserved0 = 0b00,
.p2sgn = 0b0,
.p2kvgain = 0b101,
.p2dn = 0b000000000,
.p2kv = 0b0,
},
.vco_auto = { /* 8 */
.reserved0 = 0b0,
.ctmin = 0, /* RFMD recommended change: 3 -> 0 */
.ctmax = 127, /* RFMD recommended change: 124 -> 127 */
.auto_ = 0b1,
},
.pll_ctrl = { /* 9 */
.plldy = 0b00,
.aloi = 0b0,
.relok = 0b0,
.ldlev = 0b0,
.lden = 0b1,
.tvco = 0b01000,
.pllst = 0b0,
.clkdiv = 0b000,
.divby = 0b1,
},
.pll_bias = { /* 10 */
.p2vcoi = 0b010,
.p2loi = 0b0000,
.reserved0 = 0b0,
.p1vcoi = 0b010,
.p1loi = 0b0000,
.reserved1 = 0b0,
},
.mix_cont = { /* 11 */
.reserved0 = 0b000000000,
.p2mixidd = 4,
.p1mixidd = 4,
.fulld = 0b0, /* Part on HackRF is half-duplex (single mixer) */
},
.p1_freq1 = { /* 12 */
.p1vcosel = 0b00, /* RFMD VCO bank 1 configuration from A series part */
.p1presc = 0b01,
.p1lodiv = 0b001,
.p1n = 0b000110101,
},
.p1_freq2 = { /* 13 */
.p1nmsb = 0xd89d, /* RFMD VCO bank 1 configuration from A series part */
},
.p1_freq3 = { /* 14 */
.reserved0 = 0b00000000, /* RFMD VCO bank 1 configuration from A series part */
.p1nlsb = 0x89,
},
.p2_freq1 = { /* 15 */
.p2vcosel = 0b00, /* RFMD VCO bank 2 configuration from A series part */
.p2presc = 0b01,
.p2lodiv = 0b000,
.p2n = 0b000111101,
},
.p2_freq2 = { /* 16 */
.p2nmsb = 0x89d8, /* RFMD VCO bank 2 configuration from A series part */
},
.p2_freq3 = { /* 17 */
.reserved0 = 0b00000000, /* RFMD VCO bank 2 configuration from A series part */
.p2nlsb = 0x9d,
},
.fn_ctrl = { /* 18 */
.reserved0 = 0b0,
.tzps = 0b0,
.dmode = 0b0,
.fm = 0b0,
.dith = 0b0,
.mode = 0b0,
.phsalndly = 0b10,
.phsalngain = 0b010,
.phaln = 0b1,
.sdm = 0b10,
.dithr = 0b0,
.fnz = 0b0,
},
.ext_mod = { /* 19 */
.reserved0 = 0b0000000000,
.modstep = 0b0000,
.modsetup = 0b00,
},
.fmod = { /* 20 */
.modulation = 0x0000,
},
.sdi_ctrl = { /* 21 */
.reserved0 = 0b0,
.reset = 0b0,
.reserved1 = 0b000000000,
.addr = 0b0,
.fourwire = 0b0, /* Use three pin SPI mode */
.mode = 0b1, /* Active PLL register bank 2, active mixer 2 */
.enbl = 0b0, /* Part is initially disabled */
.sipin = 0b1, /* Control MODE, ENBL from SPI bus */
},
.gpo = { /* 22 */
.lock = 0b1, /* Present LOCK signal on GPIO4/LD/DO, HackRF One test point P6 */
.gate = 0b1, /* GPOs active even when part is disabled (ENBL=0) */
.p1gpo = 0b0000001, /* Turn on GPO1 to turn *off* !ANT_BIAS (GPO numbering is one-based) */
.p2gpo = 0b0000001, /* Turn on GPO1 to turn *off* !ANT_BIAS (GPO numbering is one-based) */
},
.t_vco = { /* 23 */
.reserved0 = 0b0000000,
.curve_vco3 = 0b000,
.curve_vco2 = 0b000,
.curve_vco1 = 0b000,
},
.iqmod1 = { /* 24 */
.bufdc = 0b11,
.divbias = 0b0,
.calblk = 0b0,
.calnul = 0b0,
.calon = 0b0,
.lobias = 0b10,
.modbias = 0b010,
.modiv = 0b0,
.mod = 0b0,
.txlo = 0b0,
.bbgm = 0b0,
.ctrl = 0b0,
},
.iqmod2 = { /* 25 */
.modbuf = 0b11,
.mod = 0b11,
.calatten = 0b00,
.rctune = 0b000000,
.bbatten = 0b1111,
},
.iqmod3 = { /* 26 */
.reserved0 = 0b000,
.dacen = 0b0,
.bufdacq = 0b000000,
.bufdaci = 0b000000,
},
.iqmod4 = { /* 27 */
.bufbias2 = 0b11,
.bufbias1 = 0b11,
.moddacq = 0b000000,
.moddaci = 0b000000,
},
.t_ctrl = { /* 28 */
.reserved0 = 0b00000,
.v_test = 0b0,
.ldo_by = 0b0,
.ext_filt = 0b0,
.ref_sel = 0b0,
.filt_ctrl = 0b00,
.fc_en = 0b0,
.tbl_sel = 0b00,
.tc_en = 0b00,
},
.dev_ctrl = { /* 29 */
.reserved0 = 0b0,
.bypas = 0b0,
.ctclk = 0b0,
.dac = 0b0,
.cpd = 0b0,
.cpu = 0b0,
.rsmstopst = 0b00000,
.rsmst = 0b0,
.readsel = 0b0001,
},
.test = { /* 30 */
.lfsrd = 0b1,
.rcbyp = 0b0,
.rgbyp = 0b1, /* RFMD recommended change: 0 -> 1 */
.lfsrt = 0b0,
.lfsrgatetime = 0b0000,
.lfsrp = 0b0,
.lfsr = 0b0,
.tsel = 0b00,
.tmux = 0b000,
.ten = 0b0,
},
} };
class RFFC507x {
public:
void init();
void reset();
void flush();
void enable();
void disable();
void set_mixer_current(const uint8_t value);
void set_frequency(const rf::Frequency lo_frequency);
RegisterMap registers();
private:
spi::SPI _bus;
RegisterMap _map { default_hackrf_one };
DirtyRegisters<Register, reg_count> _dirty;
void write(const address_t reg_num, const reg_t value);
reg_t read(const address_t reg_num);
void write(const Register reg, const reg_t value);
reg_t read(const Register reg);
void flush_one(const Register reg);
reg_t readback(const Readback readback);
void init_for_best_performance();
};
} /* rffc507x */
#endif/*__RFFC507X_H__*/

View File

@ -0,0 +1,109 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "rffc507x_spi.hpp"
#include "utility.hpp"
#include "hackrf_gpio.hpp"
using namespace hackrf::one;
namespace rffc507x {
namespace spi {
void SPI::init() {
gpio_rffc5072_select.set();
gpio_rffc5072_clock.clear();
gpio_rffc5072_select.output();
gpio_rffc5072_clock.output();
gpio_rffc5072_data.input();
gpio_rffc5072_data.clear();
}
inline void SPI::select(const bool active) {
gpio_rffc5072_select.write(!active);
}
inline void SPI::direction_out() {
gpio_rffc5072_data.output();
}
inline void SPI::direction_in() {
gpio_rffc5072_data.input();
}
inline void SPI::write_bit(const bit_t value) {
gpio_rffc5072_data.write(value);
}
inline bit_t SPI::read_bit() {
return gpio_rffc5072_data.read() & 1;
}
inline bit_t SPI::transfer_bit(const bit_t bit_out) {
gpio_rffc5072_clock.clear();
write_bit(bit_out);
const bit_t bit_in = read_bit();
gpio_rffc5072_clock.set();
return bit_in;
}
data_t SPI::transfer_bits(const data_t data_out, const size_t count) {
data_t data_in = 0;
for(size_t i=0; i<count; i++) {
data_in = (data_in << 1) | transfer_bit((data_out >> (count - i - 1)) & 1);
}
return data_in;
}
data_t SPI::transfer_word(const Direction direction, const address_t address, const data_t data_out) {
select(true);
const data_t address_word =
((direction == Direction::Read) ? (1 << 7) : 0)
| (address & 0x7f)
;
direction_out();
transfer_bits(address_word, 9);
if( direction == Direction::Read ) {
direction_in();
transfer_bits(0, 2);
}
const data_t data_in = transfer_bits(data_out, 16);
if( direction == Direction::Write ) {
direction_in();
}
select(false);
transfer_bits(0, 2);
return data_in;
}
}
}

View File

@ -0,0 +1,70 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __RFFC507X_SPI_H__
#define __RFFC507X_SPI_H__
#include <cstdint>
#include <cstddef>
namespace rffc507x {
namespace spi {
using reg_t = uint16_t;
using address_t = uint8_t;
using bit_t = uint_fast8_t;
using data_t = uint32_t;
class SPI {
public:
enum class Direction {
Write = 0,
Read = 1,
};
void init();
reg_t read(const address_t address) {
return transfer_word(Direction::Read, address, 0);
}
void write(const address_t address, const reg_t value) {
transfer_word(Direction::Write, address, value);
}
private:
void select(const bool active);
void direction_out();
void direction_in();
void write_bit(const bit_t value);
bit_t read_bit();
bit_t transfer_bit(const bit_t bit_out);
data_t transfer_bits(const data_t data_out, const size_t count);
data_t transfer_word(const Direction direction, const address_t address, const data_t data_out);
};
} /* spi */
} /* rffc507x */
#endif/*__RFFC507X_SPI_H__*/

View File

@ -0,0 +1,106 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "si5351.hpp"
#include "utility.hpp"
#include <cstdint>
#include <array>
namespace si5351 {
void Si5351::reset() {
wait_for_device_ready();
write_register(Register::InterruptStatusSticky, 0x00);
write_register(Register::InterruptStatusMask, 0xf0);
update_output_enable_control();
write_register(Register::OEBPinEnableControlMask, 0xff);
write_register(Register::PLLInputSource, 0x00);
_clock_control = {
ClockControl::CLK_PDN_Power_Off, ClockControl::CLK_PDN_Power_Off,
ClockControl::CLK_PDN_Power_Off, ClockControl::CLK_PDN_Power_Off,
ClockControl::CLK_PDN_Power_Off, ClockControl::CLK_PDN_Power_Off,
ClockControl::CLK_PDN_Power_Off, ClockControl::CLK_PDN_Power_Off
};
update_all_clock_control();
write(std::array<uint8_t, 70> { Register::CLK3_0DisableState });
write(std::array<uint8_t, 14>{
Register::SpreadSpectrumParameters_Base
});
write(std::array<uint8_t, 4>{
Register::VCXOParameters_Base
});
write(std::array<uint8_t, 7>{
Register::CLKInitialPhaseOffset_Base
});
write_register(Register::CrystalInternalLoadCapacitance, 0b11010010);
write_register(Register::FanoutEnable, 0x00);
reset_plls();
}
Si5351::regvalue_t Si5351::read_register(const uint8_t reg) {
const std::array<uint8_t, 1> tx { reg };
std::array<uint8_t, 1> rx { 0x00 };
_bus.transmit(_address, tx.data(), tx.size());
_bus.receive(_address, rx.data(), rx.size());
return rx[0];
}
void Si5351::set_ms_frequency(
const size_t ms_number,
const uint32_t frequency,
const uint32_t vco_frequency,
const size_t r_div
) {
/* TODO: Factor out the VCO frequency, which should be an attribute held
* by the Si5351 object.
*/
const uint32_t a = vco_frequency / frequency;
const uint32_t remainder = vco_frequency - (frequency * a);
const uint32_t denom = gcd(remainder, frequency);
const uint32_t b = remainder / denom;
const uint32_t c = frequency / denom;
/* TODO: Switch between integer and fractional modes depending on the
* values of a and b.
*/
const MultisynthFractional ms {
.f_src = vco_frequency,
.a = a,
.b = b,
.c = c,
.r_div = r_div,
};
const auto regs = ms.reg(ms_number);
write(regs);
}
} /* namespace si5351 */

View File

@ -0,0 +1,411 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __SI5351_H__
#define __SI5351_H__
#include <cstdint>
#include <array>
#include <algorithm>
#include "ch.h"
#include "hal.h"
#include "i2c_pp.hpp"
namespace si5351 {
namespace Register {
enum {
DeviceStatus = 0,
InterruptStatusSticky = 1,
InterruptStatusMask = 2,
OutputEnableControl = 3,
OEBPinEnableControlMask = 9,
PLLInputSource = 15,
CLKControl_Base = 16,
CLKControl0 = 16,
CLKControl1 = 17,
CLKControl2 = 18,
CLKControl3 = 19,
CLKControl4 = 20,
CLKControl5 = 21,
CLKControl6 = 22,
CLKControl7 = 23,
CLK3_0DisableState = 24,
CLK7_4DisableState = 25,
MultisynthNAParameters_Base = 26,
MultisynthNBParameters_Base = 34,
Multisynth0Parameters_Base = 42,
Multisynth1Parameters_Base = 50,
Multisynth2Parameters_Base = 58,
Multisynth3Parameters_Base = 66,
Multisynth4Parameters_Base = 74,
Multisynth5Parameters_Base = 82,
Multisynth6Parameters = 90,
Multisynth7Parameters = 91,
Clock6And7OutputDivider = 92,
SpreadSpectrumParameters_Base = 149,
VCXOParameters_Base = 162,
CLKInitialPhaseOffset_Base = 165,
PLLReset = 177,
CrystalInternalLoadCapacitance = 183,
FanoutEnable = 187,
};
}
namespace DeviceStatus {
using Type = uint8_t;
enum {
REVID_Mask = (0b11 << 0),
LOS_Mask = (1 << 4),
LOS_ValidClockAtCLKIN = (0 << 4),
LOS_LossOfSignalAtCLKIN = (1 << 4),
LOL_A_Mask = (1 << 5),
LOL_A_PLLALocked = (0 << 5),
LOL_A_PLLAUnlocked = (1 << 5),
LOL_B_Mask = (1 << 6),
LOL_B_PLLBLocked = (0 << 6),
LOL_B_PLLBUnlocked = (1 << 6),
SYS_INIT_Mask = (1 << 7),
SYS_INIT_Complete = (0 << 7),
SYS_INIT_Initializing = (1 << 7),
};
}
namespace ClockControl {
using Type = uint8_t;
enum {
CLK_IDRV_Mask = (0b11 << 0),
CLK_IDRV_2mA = (0b00 << 0),
CLK_IDRV_4mA = (0b01 << 0),
CLK_IDRV_6mA = (0b10 << 0),
CLK_IDRV_8mA = (0b11 << 0),
CLK_SRC_Mask = (0b11 << 2),
CLK_SRC_XTAL = (0b00 << 2),
CLK_SRC_CLKIN = (0b01 << 2),
CLK_SRC_MS_Group = (0b10 << 2),
CLK_SRC_MS_Self = (0b11 << 2),
CLK_INV_Mask = (1 << 4),
CLK_INV_Normal = (0 << 4),
CLK_INV_Invert = (1 << 4),
MS_SRC_Mask = (1 << 5),
MS_SRC_PLLA = (0 << 5),
MS_SRC_PLLB = (1 << 5),
MS_INT_Mask = (1 << 6),
MS_INT_Fractional = (0 << 6),
MS_INT_Integer = (1 << 6),
CLK_PDN_Mask = (1 << 7),
CLK_PDN_Power_On = (0 << 7),
CLK_PDN_Power_Off = (1 << 7),
};
}
using ClockControls = std::array<ClockControl::Type, 8>;
namespace CrystalInternalLoadCapacitance {
using Type = uint8_t;
enum {
XTAL_CL_Mask = (0b11 << 6),
XTAL_CL_6pF = (0b01 << 6),
XTAL_CL_8pF = (0b10 << 6),
XTAL_CL_10pF = (0b11 << 6),
};
}
namespace PLLInputSource {
using Type = uint8_t;
enum {
PLLA_Source_Mask = (1 << 2),
PLLA_Source_XTAL = (0 << 2),
PLLA_Source_CLKIN = (1 << 2),
PLLB_Source_Mask = (1 << 3),
PLLB_Source_XTAL = (0 << 3),
PLLB_Source_CLKIN = (1 << 3),
CLKIN_Div_Mask = (0b11 << 6),
CLKIN_Div1 = (0b00 << 6),
CLKIN_Div2 = (0b01 << 6),
CLKIN_Div4 = (0b10 << 6),
CLKIN_Div8 = (0b11 << 6),
};
}
struct Inputs {
const uint32_t f_xtal;
const uint32_t f_clkin;
const uint32_t clkin_div;
constexpr uint32_t f_clkin_out() {
return f_clkin / clkin_div;
}
};
using PLLReg = std::array<uint8_t, 9>;
struct PLL {
const uint32_t f_in;
const uint32_t a;
const uint32_t b;
const uint32_t c;
constexpr uint32_t f_vco() {
return f_in * (a + (float)b / (float)c);
}
constexpr uint32_t p1() {
return 128 * a + (uint32_t)(128 * (float)b / (float)c) - 512;
}
constexpr uint32_t p2() {
return 128 * b - c * (uint32_t)(128 * (float)b / (float)c);
}
constexpr uint32_t p3() {
return c;
}
constexpr PLLReg reg(const uint8_t pll_n) {
return {
uint8_t(26 + (pll_n * 8)),
uint8_t((p3() >> 8) & 0xff),
uint8_t((p3() >> 0) & 0xff),
uint8_t((p1() >> 16) & 0x03),
uint8_t((p1() >> 8) & 0xff),
uint8_t((p1() >> 0) & 0xff),
uint8_t(
(((p3() >> 16) & 0x0f) << 4)
| ((p2() >> 16) & 0x0f)
),
uint8_t((p2() >> 8) & 0xff),
uint8_t((p2() >> 0) & 0xff),
};
}
};
using MultisynthFractionalReg = std::array<uint8_t, 9>;
struct MultisynthFractional {
const uint32_t f_src;
const uint32_t a;
const uint32_t b;
const uint32_t c;
const uint32_t r_div;
constexpr uint32_t p1() {
return 128 * a + (uint32_t)(128 * (float)b / (float)c) - 512;
}
constexpr uint32_t p2() {
return 128 * b - c * (uint32_t)(128 * (float)b / (float)c);
}
constexpr uint32_t p3() {
return c;
}
constexpr uint32_t f_out() {
return f_src / (a + (float)b / (float)c) / (1 << r_div);
}
constexpr MultisynthFractionalReg reg(const uint8_t multisynth_n) {
return {
uint8_t(42 + (multisynth_n * 8)),
uint8_t((p3() >> 8) & 0xFF),
uint8_t((p3() >> 0) & 0xFF),
uint8_t((r_div << 4) | (0 << 2) | ((p1() >> 16) & 0x3)),
uint8_t((p1() >> 8) & 0xFF),
uint8_t((p1() >> 0) & 0xFF),
uint8_t((((p3() >> 16) & 0xF) << 4) | (((p2() >> 16) & 0xF) << 0)),
uint8_t((p2() >> 8) & 0xFF),
uint8_t((p2() >> 0) & 0xFF)
};
}
};
struct MultisynthInteger {
const uint32_t f_src;
const uint32_t a;
const uint32_t r_div;
constexpr uint8_t p1() {
return a;
}
constexpr uint32_t f_out() {
return f_src / a / (1 << r_div);
}
};
using Multisynth6And7Reg = std::array<uint8_t, 4>;
constexpr Multisynth6And7Reg ms6_7_reg(
const MultisynthInteger& ms6,
const MultisynthInteger& ms7
) {
return {
Register::Multisynth6Parameters,
uint8_t(ms6.p1() & 0xff),
uint8_t(ms7.p1() & 0xff),
uint8_t(((ms7.r_div & 7) << 4) | ((ms6.r_div & 7) << 0)),
};
}
class Si5351 {
public:
using regvalue_t = uint8_t;
constexpr Si5351(I2C& bus, I2C::address_t address) :
_clock_control({
ClockControl::CLK_PDN_Power_Off, ClockControl::CLK_PDN_Power_Off,
ClockControl::CLK_PDN_Power_Off, ClockControl::CLK_PDN_Power_Off,
ClockControl::CLK_PDN_Power_Off, ClockControl::CLK_PDN_Power_Off,
ClockControl::CLK_PDN_Power_Off, ClockControl::CLK_PDN_Power_Off
}),
_bus(bus),
_address(address),
_output_enable(0x00)
{
}
void reset();
uint8_t device_status() {
return read_register(Register::DeviceStatus);
}
void wait_for_device_ready() {
while(device_status() & 0x80);
}
void enable_fanout() {
write_register(Register::FanoutEnable, 0b11010000);
}
void reset_plls() {
write_register(Register::PLLReset, 0xa0);
}
regvalue_t read_register(const uint8_t reg);
template<size_t N>
void write(const std::array<uint8_t, N>& values) {
_bus.transmit(_address, values.data(), values.size());
}
void write_register(const uint8_t reg, const regvalue_t value) {
write(std::array<uint8_t, 2>{
reg, value
});
}
void write(const size_t ms_number, const MultisynthFractional& config) {
write(config.reg(ms_number));
}
void set_ms_frequency(
const size_t ms_number,
const uint32_t frequency,
const uint32_t vco_frequency,
const size_t r_div
);
void set_crystal_internal_load_capacitance(const CrystalInternalLoadCapacitance::Type xtal_cl) {
write_register(Register::CrystalInternalLoadCapacitance, xtal_cl);
}
void set_pll_input_sources(const PLLInputSource::Type value) {
write_register(Register::PLLInputSource, value);
}
void enable_output_mask(const uint8_t mask) {
_output_enable |= mask;
update_output_enable_control();
}
void enable_output(const size_t n) {
enable_output_mask(1 << n);
}
void disable_output_mask(const uint8_t mask) {
_output_enable &= ~mask;
update_output_enable_control();
}
void disable_output(const size_t n) {
disable_output_mask(1 << n);
}
void set_clock_control(const ClockControls& clock_control) {
_clock_control = clock_control;
update_all_clock_control();
}
void enable_clock(const size_t n) {
_clock_control[n] &= ~ClockControl::CLK_PDN_Mask;
write_register(Register::CLKControl_Base + n, _clock_control[n]);
}
void disable_clock(const size_t n) {
_clock_control[n] |= ClockControl::CLK_PDN_Mask;
write_register(Register::CLKControl_Base + n, _clock_control[n]);
}
template<size_t N>
void write_registers(const uint8_t reg, const std::array<uint8_t, N>& values) {
std::array<uint8_t, N + 1> data;
data[0] = reg;
std::copy(values.cbegin(), values.cend(), data.begin() + 1);
write(data);
}
private:
std::array<uint8_t, 8> _clock_control;
I2C& _bus;
const I2C::address_t _address;
uint8_t _output_enable;
void update_output_enable_control() {
write_register(Register::OutputEnableControl, ~_output_enable);
}
void update_all_clock_control() {
write_registers(Register::CLKControl_Base, _clock_control);
}
};
}
#endif/*__SI5351_H__*/

View File

@ -0,0 +1,540 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "spectrum_color_lut.hpp"
const std::array<ui::Color, 256> spectrum_rgb2_lut { {
{ 0, 0, 128 },
{ 0, 0, 132 },
{ 0, 0, 136 },
{ 0, 0, 140 },
{ 0, 0, 144 },
{ 0, 0, 148 },
{ 0, 0, 152 },
{ 0, 0, 156 },
{ 0, 0, 160 },
{ 0, 0, 164 },
{ 0, 0, 168 },
{ 0, 0, 172 },
{ 0, 0, 176 },
{ 0, 0, 180 },
{ 0, 0, 184 },
{ 0, 0, 188 },
{ 0, 0, 192 },
{ 0, 0, 195 },
{ 0, 0, 199 },
{ 0, 0, 203 },
{ 0, 0, 207 },
{ 0, 0, 211 },
{ 0, 0, 215 },
{ 0, 0, 219 },
{ 0, 0, 223 },
{ 0, 0, 227 },
{ 0, 0, 231 },
{ 0, 0, 235 },
{ 0, 0, 239 },
{ 0, 0, 243 },
{ 0, 0, 247 },
{ 0, 0, 251 },
{ 0, 0, 255 },
{ 0, 4, 255 },
{ 0, 8, 255 },
{ 0, 12, 255 },
{ 0, 16, 255 },
{ 0, 20, 255 },
{ 0, 24, 255 },
{ 0, 28, 255 },
{ 0, 32, 255 },
{ 0, 36, 255 },
{ 0, 40, 255 },
{ 0, 44, 255 },
{ 0, 48, 255 },
{ 0, 52, 255 },
{ 0, 56, 255 },
{ 0, 60, 255 },
{ 0, 64, 255 },
{ 0, 68, 255 },
{ 0, 72, 255 },
{ 0, 76, 255 },
{ 0, 80, 255 },
{ 0, 84, 255 },
{ 0, 88, 255 },
{ 0, 92, 255 },
{ 0, 96, 255 },
{ 0, 100, 255 },
{ 0, 104, 255 },
{ 0, 108, 255 },
{ 0, 112, 255 },
{ 0, 116, 255 },
{ 0, 120, 255 },
{ 0, 124, 255 },
{ 0, 128, 255 },
{ 0, 131, 255 },
{ 0, 135, 255 },
{ 0, 139, 255 },
{ 0, 143, 255 },
{ 0, 147, 255 },
{ 0, 151, 255 },
{ 0, 155, 255 },
{ 0, 159, 255 },
{ 0, 163, 255 },
{ 0, 167, 255 },
{ 0, 171, 255 },
{ 0, 175, 255 },
{ 0, 179, 255 },
{ 0, 183, 255 },
{ 0, 187, 255 },
{ 0, 191, 255 },
{ 0, 195, 255 },
{ 0, 199, 255 },
{ 0, 203, 255 },
{ 0, 207, 255 },
{ 0, 211, 255 },
{ 0, 215, 255 },
{ 0, 219, 255 },
{ 0, 223, 255 },
{ 0, 227, 255 },
{ 0, 231, 255 },
{ 0, 235, 255 },
{ 0, 239, 255 },
{ 0, 243, 255 },
{ 0, 247, 255 },
{ 0, 251, 255 },
{ 0, 255, 255 },
{ 4, 255, 251 },
{ 8, 255, 247 },
{ 12, 255, 243 },
{ 16, 255, 239 },
{ 20, 255, 235 },
{ 24, 255, 231 },
{ 28, 255, 227 },
{ 32, 255, 223 },
{ 36, 255, 219 },
{ 40, 255, 215 },
{ 44, 255, 211 },
{ 48, 255, 207 },
{ 52, 255, 203 },
{ 56, 255, 199 },
{ 60, 255, 195 },
{ 64, 255, 191 },
{ 68, 255, 187 },
{ 72, 255, 183 },
{ 76, 255, 179 },
{ 80, 255, 175 },
{ 84, 255, 171 },
{ 88, 255, 167 },
{ 92, 255, 163 },
{ 96, 255, 159 },
{ 100, 255, 155 },
{ 104, 255, 151 },
{ 108, 255, 147 },
{ 112, 255, 143 },
{ 116, 255, 139 },
{ 120, 255, 135 },
{ 124, 255, 131 },
{ 128, 255, 128 },
{ 131, 255, 124 },
{ 135, 255, 120 },
{ 139, 255, 116 },
{ 143, 255, 112 },
{ 147, 255, 108 },
{ 151, 255, 104 },
{ 155, 255, 100 },
{ 159, 255, 96 },
{ 163, 255, 92 },
{ 167, 255, 88 },
{ 171, 255, 84 },
{ 175, 255, 80 },
{ 179, 255, 76 },
{ 183, 255, 72 },
{ 187, 255, 68 },
{ 191, 255, 64 },
{ 195, 255, 60 },
{ 199, 255, 56 },
{ 203, 255, 52 },
{ 207, 255, 48 },
{ 211, 255, 44 },
{ 215, 255, 40 },
{ 219, 255, 36 },
{ 223, 255, 32 },
{ 227, 255, 28 },
{ 231, 255, 24 },
{ 235, 255, 20 },
{ 239, 255, 16 },
{ 243, 255, 12 },
{ 247, 255, 8 },
{ 251, 255, 4 },
{ 255, 255, 0 },
{ 255, 251, 0 },
{ 255, 247, 0 },
{ 255, 243, 0 },
{ 255, 239, 0 },
{ 255, 235, 0 },
{ 255, 231, 0 },
{ 255, 227, 0 },
{ 255, 223, 0 },
{ 255, 219, 0 },
{ 255, 215, 0 },
{ 255, 211, 0 },
{ 255, 207, 0 },
{ 255, 203, 0 },
{ 255, 199, 0 },
{ 255, 195, 0 },
{ 255, 191, 0 },
{ 255, 187, 0 },
{ 255, 183, 0 },
{ 255, 179, 0 },
{ 255, 175, 0 },
{ 255, 171, 0 },
{ 255, 167, 0 },
{ 255, 163, 0 },
{ 255, 159, 0 },
{ 255, 155, 0 },
{ 255, 151, 0 },
{ 255, 147, 0 },
{ 255, 143, 0 },
{ 255, 139, 0 },
{ 255, 135, 0 },
{ 255, 131, 0 },
{ 255, 128, 0 },
{ 255, 124, 0 },
{ 255, 120, 0 },
{ 255, 116, 0 },
{ 255, 112, 0 },
{ 255, 108, 0 },
{ 255, 104, 0 },
{ 255, 100, 0 },
{ 255, 96, 0 },
{ 255, 92, 0 },
{ 255, 88, 0 },
{ 255, 84, 0 },
{ 255, 80, 0 },
{ 255, 76, 0 },
{ 255, 72, 0 },
{ 255, 68, 0 },
{ 255, 64, 0 },
{ 255, 60, 0 },
{ 255, 56, 0 },
{ 255, 52, 0 },
{ 255, 48, 0 },
{ 255, 44, 0 },
{ 255, 40, 0 },
{ 255, 36, 0 },
{ 255, 32, 0 },
{ 255, 28, 0 },
{ 255, 24, 0 },
{ 255, 20, 0 },
{ 255, 16, 0 },
{ 255, 12, 0 },
{ 255, 8, 0 },
{ 255, 4, 0 },
{ 255, 0, 0 },
{ 251, 0, 0 },
{ 247, 0, 0 },
{ 243, 0, 0 },
{ 239, 0, 0 },
{ 235, 0, 0 },
{ 231, 0, 0 },
{ 227, 0, 0 },
{ 223, 0, 0 },
{ 219, 0, 0 },
{ 215, 0, 0 },
{ 211, 0, 0 },
{ 207, 0, 0 },
{ 203, 0, 0 },
{ 199, 0, 0 },
{ 195, 0, 0 },
{ 192, 0, 0 },
{ 188, 0, 0 },
{ 184, 0, 0 },
{ 180, 0, 0 },
{ 176, 0, 0 },
{ 172, 0, 0 },
{ 168, 0, 0 },
{ 164, 0, 0 },
{ 160, 0, 0 },
{ 156, 0, 0 },
{ 152, 0, 0 },
{ 148, 0, 0 },
{ 144, 0, 0 },
{ 140, 0, 0 },
{ 136, 0, 0 },
{ 132, 0, 0 },
} };
const std::array<ui::Color, 256> spectrum_rgb3_lut { {
{ 0, 0, 0 },
{ 0, 0, 3 },
{ 0, 0, 6 },
{ 0, 0, 9 },
{ 0, 0, 12 },
{ 0, 0, 15 },
{ 0, 0, 18 },
{ 0, 0, 21 },
{ 0, 0, 24 },
{ 0, 0, 27 },
{ 0, 0, 30 },
{ 0, 0, 33 },
{ 0, 0, 36 },
{ 0, 0, 39 },
{ 0, 0, 42 },
{ 0, 0, 45 },
{ 0, 0, 48 },
{ 0, 0, 51 },
{ 0, 0, 54 },
{ 0, 0, 57 },
{ 0, 0, 60 },
{ 0, 0, 63 },
{ 0, 0, 66 },
{ 0, 0, 69 },
{ 0, 0, 72 },
{ 0, 0, 75 },
{ 0, 0, 78 },
{ 0, 0, 81 },
{ 0, 0, 84 },
{ 0, 0, 87 },
{ 0, 0, 90 },
{ 0, 0, 93 },
{ 0, 0, 96 },
{ 0, 0, 99 },
{ 0, 0, 102 },
{ 0, 0, 105 },
{ 0, 0, 108 },
{ 0, 0, 111 },
{ 0, 0, 114 },
{ 0, 0, 117 },
{ 0, 0, 120 },
{ 0, 0, 123 },
{ 0, 0, 126 },
{ 0, 0, 129 },
{ 0, 0, 132 },
{ 0, 0, 135 },
{ 0, 0, 138 },
{ 0, 0, 141 },
{ 0, 0, 144 },
{ 0, 0, 147 },
{ 0, 0, 150 },
{ 0, 0, 153 },
{ 0, 0, 156 },
{ 0, 0, 159 },
{ 0, 0, 162 },
{ 0, 0, 165 },
{ 0, 0, 168 },
{ 0, 0, 171 },
{ 0, 0, 174 },
{ 0, 0, 177 },
{ 0, 0, 180 },
{ 0, 0, 183 },
{ 0, 0, 186 },
{ 0, 0, 189 },
{ 0, 0, 192 },
{ 0, 0, 195 },
{ 0, 0, 198 },
{ 0, 0, 201 },
{ 0, 0, 204 },
{ 0, 0, 207 },
{ 0, 0, 210 },
{ 0, 0, 213 },
{ 0, 0, 216 },
{ 0, 0, 219 },
{ 0, 0, 222 },
{ 0, 0, 225 },
{ 0, 0, 228 },
{ 0, 0, 231 },
{ 0, 0, 234 },
{ 0, 0, 237 },
{ 0, 0, 240 },
{ 0, 0, 243 },
{ 0, 0, 246 },
{ 0, 0, 249 },
{ 0, 0, 252 },
{ 0, 0, 255 },
{ 0, 3, 252 },
{ 0, 6, 249 },
{ 0, 9, 246 },
{ 0, 12, 243 },
{ 0, 15, 240 },
{ 0, 18, 237 },
{ 0, 21, 234 },
{ 0, 24, 231 },
{ 0, 27, 228 },
{ 0, 30, 225 },
{ 0, 33, 222 },
{ 0, 36, 219 },
{ 0, 39, 216 },
{ 0, 42, 213 },
{ 0, 45, 210 },
{ 0, 48, 207 },
{ 0, 51, 204 },
{ 0, 54, 201 },
{ 0, 57, 198 },
{ 0, 60, 195 },
{ 0, 63, 192 },
{ 0, 66, 189 },
{ 0, 69, 186 },
{ 0, 72, 183 },
{ 0, 75, 180 },
{ 0, 78, 177 },
{ 0, 81, 174 },
{ 0, 84, 171 },
{ 0, 87, 168 },
{ 0, 90, 165 },
{ 0, 93, 162 },
{ 0, 96, 159 },
{ 0, 99, 156 },
{ 0, 102, 153 },
{ 0, 105, 150 },
{ 0, 108, 147 },
{ 0, 111, 144 },
{ 0, 114, 141 },
{ 0, 117, 138 },
{ 0, 120, 135 },
{ 0, 123, 132 },
{ 0, 126, 129 },
{ 0, 129, 126 },
{ 0, 132, 123 },
{ 0, 135, 120 },
{ 0, 138, 117 },
{ 0, 141, 114 },
{ 0, 144, 111 },
{ 0, 147, 108 },
{ 0, 150, 105 },
{ 0, 153, 102 },
{ 0, 156, 99 },
{ 0, 159, 96 },
{ 0, 162, 93 },
{ 0, 165, 90 },
{ 0, 168, 87 },
{ 0, 171, 84 },
{ 0, 174, 81 },
{ 0, 177, 78 },
{ 0, 180, 75 },
{ 0, 183, 72 },
{ 0, 186, 69 },
{ 0, 189, 66 },
{ 0, 192, 63 },
{ 0, 195, 60 },
{ 0, 198, 57 },
{ 0, 201, 54 },
{ 0, 204, 51 },
{ 0, 207, 48 },
{ 0, 210, 45 },
{ 0, 213, 42 },
{ 0, 216, 39 },
{ 0, 219, 36 },
{ 0, 222, 33 },
{ 0, 225, 30 },
{ 0, 228, 27 },
{ 0, 231, 24 },
{ 0, 234, 21 },
{ 0, 237, 18 },
{ 0, 240, 15 },
{ 0, 243, 12 },
{ 0, 246, 9 },
{ 0, 249, 6 },
{ 0, 252, 3 },
{ 0, 255, 0 },
{ 3, 252, 0 },
{ 6, 249, 0 },
{ 9, 246, 0 },
{ 12, 243, 0 },
{ 15, 240, 0 },
{ 18, 237, 0 },
{ 21, 234, 0 },
{ 24, 231, 0 },
{ 27, 228, 0 },
{ 30, 225, 0 },
{ 33, 222, 0 },
{ 36, 219, 0 },
{ 39, 216, 0 },
{ 42, 213, 0 },
{ 45, 210, 0 },
{ 48, 207, 0 },
{ 51, 204, 0 },
{ 54, 201, 0 },
{ 57, 198, 0 },
{ 60, 195, 0 },
{ 63, 192, 0 },
{ 66, 189, 0 },
{ 69, 186, 0 },
{ 72, 183, 0 },
{ 75, 180, 0 },
{ 78, 177, 0 },
{ 81, 174, 0 },
{ 84, 171, 0 },
{ 87, 168, 0 },
{ 90, 165, 0 },
{ 93, 162, 0 },
{ 96, 159, 0 },
{ 99, 156, 0 },
{ 102, 153, 0 },
{ 105, 150, 0 },
{ 108, 147, 0 },
{ 111, 144, 0 },
{ 114, 141, 0 },
{ 117, 138, 0 },
{ 120, 135, 0 },
{ 123, 132, 0 },
{ 126, 129, 0 },
{ 129, 126, 0 },
{ 132, 123, 0 },
{ 135, 120, 0 },
{ 138, 117, 0 },
{ 141, 114, 0 },
{ 144, 111, 0 },
{ 147, 108, 0 },
{ 150, 105, 0 },
{ 153, 102, 0 },
{ 156, 99, 0 },
{ 159, 96, 0 },
{ 162, 93, 0 },
{ 165, 90, 0 },
{ 168, 87, 0 },
{ 171, 84, 0 },
{ 174, 81, 0 },
{ 177, 78, 0 },
{ 180, 75, 0 },
{ 183, 72, 0 },
{ 186, 69, 0 },
{ 189, 66, 0 },
{ 192, 63, 0 },
{ 195, 60, 0 },
{ 198, 57, 0 },
{ 201, 54, 0 },
{ 204, 51, 0 },
{ 207, 48, 0 },
{ 210, 45, 0 },
{ 213, 42, 0 },
{ 216, 39, 0 },
{ 219, 36, 0 },
{ 222, 33, 0 },
{ 225, 30, 0 },
{ 228, 27, 0 },
{ 231, 24, 0 },
{ 234, 21, 0 },
{ 237, 18, 0 },
{ 240, 15, 0 },
{ 243, 12, 0 },
{ 246, 9, 0 },
{ 249, 6, 0 },
{ 252, 3, 0 },
{ 255, 0, 0 },
} };

View File

@ -0,0 +1,32 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __SPECTRUM_COLOR_LUT_H__
#define __SPECTRUM_COLOR_LUT_H__
#include "ui.hpp"
#include <array>
extern const std::array<ui::Color, 256> spectrum_rgb2_lut;
extern const std::array<ui::Color, 256> spectrum_rgb3_lut;
#endif/*__SPECTRUM_COLOR_LUT_H__*/

View File

@ -0,0 +1,77 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __SPI_ARBITER_H__
#define __SPI_ARBITER_H__
#include <cstddef>
#include "spi_pp.hpp"
namespace spi {
namespace arbiter {
class Arbiter {
public:
constexpr Arbiter(
SPI& bus
) : _bus(bus),
_config(nullptr)
{
}
void transfer(const SPIConfig* const config, void* const data, const size_t count) {
if( config != _config ) {
_bus.stop();
_bus.start(*config);
_config = config;
}
_bus.transfer(data, count);
}
private:
SPI& _bus;
const SPIConfig* _config;
};
class Target {
public:
constexpr Target(
Arbiter& arbiter,
const SPIConfig& config
) : _arbiter(arbiter),
_config(config)
{
}
void transfer(void* const data, const size_t count) {
_arbiter.transfer(&_config, data, count);
}
private:
Arbiter& _arbiter;
const SPIConfig _config;
};
} /* arbiter */
} /* spi */
#endif/*__SPI_ARBITER_H__*/

View File

@ -0,0 +1,22 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "spi_pp.hpp"

View File

@ -0,0 +1,56 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __SPI_PP_H__
#define __SPI_PP_H__
#include <cstdint>
#include "ch.h"
#include "hal.h"
class SPI {
public:
constexpr SPI(SPIDriver* const driver) :
_driver(driver) {
}
void start(const SPIConfig& config) {
spiStart(_driver, &config);
}
void stop() {
spiStop(_driver);
}
void transfer(void* const data, const size_t count) {
spiAcquireBus(_driver);
spiSelect(_driver);
spiExchange(_driver, count, data, data);
spiUnselect(_driver);
spiReleaseBus(_driver);
}
private:
SPIDriver* const _driver;
};
#endif/*__SPI_PP_H__*/

View File

@ -0,0 +1,119 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "touch.hpp"
namespace touch {
struct Metrics {
const float x;
const float y;
const float r;
};
static Metrics calculate_metrics(const Frame frame) {
/* TODO: Yikes! M0 doesn't have floating point, so this code is
* expensive! On the other hand, it seems to be working well (and
* fast *enough*?), so maybe leave it alone at least for now.
*/
const auto x_max = frame.x.xp;
const auto x_min = frame.x.xn;
const auto x_range = x_max - x_min;
const auto x_position = (frame.x.yp + frame.x.yn) / 2;
const float x_norm = float(x_position - x_min) / x_range;
const auto y_max = frame.y.yn;
const auto y_min = frame.y.yp;
const auto y_range = y_max - y_min;
const auto y_position = (frame.y.xp + frame.y.xn) / 2;
const float y_norm = float(y_position - y_min) / y_range;
const auto z_max = frame.pressure.yp;
const auto z_min = frame.pressure.xn;
const auto z_range = z_max - z_min;
const auto z1_position = frame.pressure.xp;
const float z1_norm = float(z1_position - z_min) / z_range;
const auto z2_position = frame.pressure.yn;
const float z2_norm = float(z2_position - z_min) / z_range;
const float r_x_plate = 330.0f;
//const float r_y_plate = 600.0f;
const float r_touch = r_x_plate * x_norm * (z2_norm / z1_norm - 1.0f);
return {
.x = x_norm,
.y = y_norm,
.r = r_touch,
};
}
void Manager::feed(const Frame frame) {
// touch_debounce.feed(touch_raw);
const auto touch_raw = frame.touch;
//const auto touch_stable = touch_debounce.state();
const auto touch_stable = frame.touch;
bool touch_pressure = false;
// Only feed coordinate averaging if there's a touch.
// TODO: Separate threshold to gate coordinates for filtering?
if( touch_raw ) {
const auto metrics = calculate_metrics(frame);
// TODO: Add touch pressure hysteresis?
touch_pressure = (metrics.r < r_touch_threshold);
if( touch_pressure ) {
const float x = width_pixels * (metrics.x - calib_x_low) / calib_x_range;
filter_x.feed(x);
const float y = height_pixels * (calib_y_high - metrics.y) / calib_y_range;
filter_y.feed(y);
}
} else {
filter_x.reset();
filter_y.reset();
}
switch(state) {
case State::NoTouch:
if( touch_stable && touch_pressure ) {
if( point_stable() ) {
state = State::TouchDetected;
touch_started();
}
}
break;
case State::TouchDetected:
if( touch_stable && touch_pressure ) {
touch_moved();
} else {
state = State::NoTouch;
touch_ended();
}
break;
default:
state = State::NoTouch;
break;
}
}
} /* namespace touch */

View File

@ -0,0 +1,233 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __TOUCH_H__
#define __TOUCH_H__
#include <cstdint>
#include <cstddef>
#include <algorithm>
#include <array>
#include <functional>
#include "debounce.hpp"
#include "ui.hpp"
namespace touch {
using sample_t = uint16_t;
constexpr sample_t sample_max = 1023;
constexpr sample_t touch_threshold = sample_max * 0.3f;
struct Samples {
sample_t xp;
sample_t xn;
sample_t yp;
sample_t yn;
constexpr Samples(
) : Samples { 0 }
{
}
constexpr Samples(
uint32_t v
) : xp { static_cast<sample_t>(v) },
xn { static_cast<sample_t>(v) },
yp { static_cast<sample_t>(v) },
yn { static_cast<sample_t>(v) }
{
}
constexpr Samples(
uint32_t xp,
uint32_t xn,
uint32_t yp,
uint32_t yn
) : xp { static_cast<sample_t>(xp) },
xn { static_cast<sample_t>(xn) },
yp { static_cast<sample_t>(yp) },
yn { static_cast<sample_t>(yn) }
{
}
Samples& operator +=(const Samples& r) {
xp += r.xp;
xn += r.xn;
yp += r.yp;
yn += r.yn;
return *this;
}
Samples operator/(const unsigned int r) const {
return {
static_cast<sample_t>(xp / r),
static_cast<sample_t>(xn / r),
static_cast<sample_t>(yp / r),
static_cast<sample_t>(yn / r)
};
}
Samples operator>>(const size_t n) const {
return {
static_cast<sample_t>(xp >> n),
static_cast<sample_t>(xn >> n),
static_cast<sample_t>(yp >> n),
static_cast<sample_t>(yn >> n)
};
}
};
struct Frame {
Samples pressure { };
Samples x { };
Samples y { };
bool touch { false };
};
template<size_t N>
class Filter {
public:
constexpr Filter(
) : history(),
history_history { 0 },
accumulator { 0 },
n { 0 }
{
}
void reset() {
history.fill(0);
history_history = 0;
accumulator = 0;
n = 0;
}
void feed(const sample_t value) {
accumulator = accumulator + value - history[n];
history[n] = value;
n = (n + 1) % history.size();
history_history = (history_history << 1) | 1U;
}
uint32_t value() const {
return accumulator / N;
}
bool stable(const uint32_t bound) const {
if( history_valid() ) {
const auto minmax = std::minmax_element(history.cbegin(), history.cend());
const auto min = *minmax.first;
const auto max = *minmax.second;
const uint32_t delta = max - min;
return (delta < bound);
} else {
return false;
}
}
private:
static constexpr uint32_t history_history_mask { (1U << N) - 1 };
std::array<sample_t, N> history;
uint32_t history_history;
uint32_t accumulator;
size_t n;
bool history_valid() const {
return (history_history & history_history_mask) == history_history_mask;
}
};
class Manager {
public:
std::function<void(ui::TouchEvent)> on_event;
void feed(const Frame frame);
private:
enum State {
NoTouch,
TouchDetected,
};
static constexpr uint32_t width_pixels = 240;
static constexpr uint32_t height_pixels = 320;
static constexpr float r_touch_threshold = 0x1000;
static constexpr size_t touch_count_threshold { 4 };
static constexpr uint32_t touch_stable_bound { 4 };
static constexpr float calib_x_low = 0.07f;
static constexpr float calib_x_high = 0.94f;
static constexpr float calib_x_range = calib_x_high - calib_x_low;
static constexpr float calib_y_low = 0.04f;
static constexpr float calib_y_high = 0.91f;
static constexpr float calib_y_range = calib_y_high - calib_y_low;
// Ensure filter length is equal or less than touch_count_threshold,
// or coordinates from the last touch will be in the initial averages.
Filter<touch_count_threshold> filter_x;
Filter<touch_count_threshold> filter_y;
//Debounce touch_debounce;
State state { State::NoTouch };
bool point_stable() const {
return filter_x.stable(touch_stable_bound)
&& filter_y.stable(touch_stable_bound);
}
ui::Point filtered_point() const {
return {
static_cast<ui::Coord>(filter_x.value()),
static_cast<ui::Coord>(filter_y.value())
};
}
void touch_started() {
fire_event(ui::TouchEvent::Type::Start);
}
void touch_moved() {
fire_event(ui::TouchEvent::Type::Move);
}
void touch_ended() {
fire_event(ui::TouchEvent::Type::End);
}
void fire_event(ui::TouchEvent::Type type) {
if( on_event ) {
on_event({ filtered_point(), type });
}
}
};
} /* namespace touch */
#endif/*__TOUCH_H__*/

View File

@ -0,0 +1,101 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "touch.hpp"
#include <cstdint>
#include "adc.hpp"
#include "utility.hpp"
#include "hal.h"
using namespace lpc43xx;
// #include "hackrf_gpio.hpp"
#include "hackrf_hal.hpp"
using namespace hackrf::one;
#include "portapack_adc.hpp"
#include "portapack_shared_memory.hpp"
namespace touch {
namespace adc {
constexpr uint8_t adc0_sel =
(1 << portapack::adc0_touch_xp_input)
| (1 << portapack::adc0_touch_xn_input)
| (1 << portapack::adc0_touch_yp_input)
| (1 << portapack::adc0_touch_yn_input)
;
const auto adc0_interrupt_mask = flp2(adc0_sel);
constexpr lpc43xx::adc::CR adc0_cr {
.sel = adc0_sel,
.clkdiv = 49, /* 400kHz sample rate, 2.5us/sample @ 200MHz PCLK */
.resolution = 9, /* Ten clocks */
.edge = 0,
};
constexpr lpc43xx::adc::Config adc0_config {
.cr = adc0_cr,
};
void init() {
adc0.clock_enable();
adc0.interrupts_disable();
adc0.power_up(adc0_config);
adc0.interrupts_enable(adc0_interrupt_mask);
//nvicEnableVector(ADC0_IRQn, CORTEX_PRIORITY_MASK(LPC_ADC0_IRQ_PRIORITY));
}
void start() {
adc0.start_burst();
}
// static constexpr bool monitor_overruns_and_not_dones = false;
Samples get() {
const auto& frame = shared_memory.touch_adc_frame;
const auto xp = frame.dr[portapack::adc0_touch_xp_input];
const auto xn = frame.dr[portapack::adc0_touch_xn_input];
const auto yp = frame.dr[portapack::adc0_touch_yp_input];
const auto yn = frame.dr[portapack::adc0_touch_yn_input];
// if( monitor_overruns_and_not_dones ) {
// const auto dr_and = xp & xn & yp & yn;
// const auto dr_or = xp | xn | yp | yn;
// const bool done = (dr_and >> 31) & 1;
// const bool overrun = (dr_or >> 30) & 1;
// led_tx.write(overrun);
// led_rx.write(!done);
// }
return {
(xp >> 6) & 0x3ff,
(xn >> 6) & 0x3ff,
(yp >> 6) & 0x3ff,
(yn >> 6) & 0x3ff,
};
}
} /* namespace adc */
} /* namespace touch */

View File

@ -0,0 +1,38 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __TOUCH_ADC_H__
#define __TOUCH_ADC_H__
#include "touch.hpp"
namespace touch {
namespace adc {
void init();
void start();
Samples get();
} /* namespace adc */
} /* namespace touch */
#endif/*__TOUCH_ADC_H__*/

View File

@ -0,0 +1,100 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ch.h"
#include "tuning.hpp"
#include "utility.hpp"
namespace tuning {
namespace config {
namespace {
constexpr rf::Frequency low_band_second_lo_frequency(const rf::Frequency target_frequency) {
return 2650000000 - (target_frequency / 7);
}
constexpr rf::Frequency high_band_second_lo_regions_2_and_3(const rf::Frequency target_frequency) {
return (target_frequency < 5100000000)
? (2350000000 + ((target_frequency - 3600000000) / 5))
: (2500000000 + ((target_frequency - 5100000000) / 9))
;
}
constexpr rf::Frequency high_band_second_lo_frequency(const rf::Frequency target_frequency) {
return (target_frequency < 3600000000)
? (2150000000 + (((target_frequency - 2750000000) * 60) / 85))
: high_band_second_lo_regions_2_and_3(target_frequency)
;
}
Config low_band(const rf::Frequency target_frequency) {
const rf::Frequency first_lo_frequency = target_frequency + low_band_second_lo_frequency(target_frequency);
const rf::Frequency second_lo_frequency = first_lo_frequency - target_frequency;
return {
.first_lo_frequency = first_lo_frequency,
.second_lo_frequency = second_lo_frequency,
.rf_path_band = rf::path::Band::Low,
.baseband_q_invert = true,
};
}
Config mid_band(const rf::Frequency target_frequency) {
return {
.first_lo_frequency = 0,
.second_lo_frequency = target_frequency,
.rf_path_band = rf::path::Band::Mid,
.baseband_q_invert = false,
};
}
Config high_band(const rf::Frequency target_frequency) {
const rf::Frequency first_lo_frequency = target_frequency - high_band_second_lo_frequency(target_frequency);
const rf::Frequency second_lo_frequency = target_frequency - first_lo_frequency;
return {
.first_lo_frequency = first_lo_frequency,
.second_lo_frequency = second_lo_frequency,
.rf_path_band = rf::path::Band::High,
.baseband_q_invert = false,
};
}
} /* namespace */
Config create(const rf::Frequency target_frequency) {
/* TODO: This is some lame code. */
if( target_frequency < rf::path::band_low.min ) {
return { };
} else if( target_frequency < rf::path::band_low.max ) {
return low_band(target_frequency);
} else if( target_frequency < rf::path::band_mid.max ) {
return mid_band(target_frequency);
} else if( target_frequency < rf::path::band_high.max ) {
return high_band(target_frequency);
} else {
return { };
}
}
} /* namespace config */
} /* namespace tuning */

View File

@ -0,0 +1,67 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __TUNING_H__
#define __TUNING_H__
#include "rf_path.hpp"
namespace tuning {
namespace config {
struct Config {
/* Empty config to denote an error, in lieu of throwing an exception. */
constexpr Config(
) : first_lo_frequency(0),
second_lo_frequency(0),
rf_path_band(rf::path::Band::Mid),
baseband_q_invert(false)
{
}
constexpr Config(
rf::Frequency first_lo_frequency,
rf::Frequency second_lo_frequency,
rf::path::Band rf_path_band,
bool baseband_q_invert
) : first_lo_frequency(first_lo_frequency),
second_lo_frequency(second_lo_frequency),
rf_path_band(rf_path_band),
baseband_q_invert(baseband_q_invert)
{
}
bool is_valid() const {
return (second_lo_frequency != 0);
}
const rf::Frequency first_lo_frequency;
const rf::Frequency second_lo_frequency;
const rf::path::Band rf_path_band;
const bool baseband_q_invert;
};
Config create(const rf::Frequency target_frequency);
} /* namespace config */
} /* namespace tuning */
#endif/*__TUNING_H__*/

View File

@ -0,0 +1,90 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_audio.hpp"
#include <algorithm>
namespace ui {
void Audio::on_show() {
context().message_map[Message::ID::AudioStatistics] = [this](const Message* const p) {
this->on_statistics_update(static_cast<const AudioStatisticsMessage*>(p)->statistics);
};
}
void Audio::on_hide() {
context().message_map[Message::ID::AudioStatistics] = nullptr;
}
void Audio::paint(Painter& painter) {
const auto r = screen_rect();
const int32_t db_min = -r.width();
const int32_t x_0 = 0;
const int32_t x_rms = std::max(x_0, rms_db_ - db_min);
const int32_t x_max = std::max(x_rms + 1, max_db_ - db_min);
const int32_t x_lim = r.width();
const Rect r0 {
static_cast<ui::Coord>(r.left() + x_0), r.top(),
static_cast<ui::Dim>(x_rms - x_0), r.height()
};
painter.fill_rectangle(
r0,
Color::green()
);
const Rect r1 {
static_cast<ui::Coord>(r.left() + x_rms), r.top(),
1, r.height()
};
painter.fill_rectangle(
r1,
Color::black()
);
const Rect r2 {
static_cast<ui::Coord>(r.left() + x_rms + 1), r.top(),
static_cast<ui::Dim>(x_max - (x_rms + 1)), r.height()
};
painter.fill_rectangle(
r2,
Color::red()
);
const Rect r3 {
static_cast<ui::Coord>(r.left() + x_max), r.top(),
static_cast<ui::Dim>(x_lim - x_max), r.height()
};
painter.fill_rectangle(
r3,
Color::black()
);
}
void Audio::on_statistics_update(const AudioStatistics& statistics) {
rms_db_ = statistics.rms_db;
max_db_ = statistics.max_db;
set_dirty();
}
} /* namespace ui */

View File

@ -0,0 +1,57 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __UI_AUDIO_H__
#define __UI_AUDIO_H__
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_painter.hpp"
#include <cstdint>
namespace ui {
class Audio : public Widget {
public:
constexpr Audio(
const Rect parent_rect
) : Widget { parent_rect },
rms_db_ { -120 },
max_db_ { -120 }
{
}
void on_show() override;
void on_hide() override;
void paint(Painter& painter) override;
private:
int32_t rms_db_;
int32_t max_db_;
void on_statistics_update(const AudioStatistics& statistics);
};
}
#endif/*__UI_AUDIO_H__*/

View File

@ -0,0 +1,79 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_channel.hpp"
#include <algorithm>
namespace ui {
void Channel::on_show() {
context().message_map[Message::ID::ChannelStatistics] = [this](const Message* const p) {
this->on_statistics_update(static_cast<const ChannelStatisticsMessage*>(p)->statistics);
};
}
void Channel::on_hide() {
context().message_map[Message::ID::ChannelStatistics] = nullptr;
}
void Channel::paint(Painter& painter) {
const auto r = screen_rect();
const int32_t db_min = -r.width();
const int32_t x_0 = 0;
const int32_t x_max = std::max(x_0, max_db_ - db_min);
const int32_t x_lim = r.width();
const Rect r0 {
static_cast<ui::Coord>(r.left() + x_0), r.top(),
static_cast<ui::Dim>(x_max - x_0), r.height()
};
painter.fill_rectangle(
r0,
Color::blue()
);
const Rect r1 {
static_cast<ui::Coord>(r.left() + x_max), r.top(),
1, r.height()
};
painter.fill_rectangle(
r1,
Color::white()
);
const Rect r2 {
static_cast<ui::Coord>(r.left() + x_max + 1), r.top(),
static_cast<ui::Dim>(x_lim - (x_max + 1)), r.height()
};
painter.fill_rectangle(
r2,
Color::black()
);
}
void Channel::on_statistics_update(const ChannelStatistics& statistics) {
max_db_ = statistics.max_db;
set_dirty();
}
} /* namespace ui */

View File

@ -0,0 +1,55 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __UI_CHANNEL_H__
#define __UI_CHANNEL_H__
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_painter.hpp"
#include <cstdint>
namespace ui {
class Channel : public Widget {
public:
constexpr Channel(
const Rect parent_rect
) : Widget { parent_rect },
max_db_ { -120 }
{
}
void on_show() override;
void on_hide() override;
void paint(Painter& painter) override;
private:
int32_t max_db_;
void on_statistics_update(const ChannelStatistics& statistics);
};
}
#endif/*__UI_CHANNEL_H__*/

View File

@ -0,0 +1,66 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_console.hpp"
namespace ui {
void Console::write(const std::string message) {
(void)message;
}
void Console::writeln(const std::string message) {
write(message);
crlf();
}
void Console::paint(Painter& painter) {
(void)painter;
/*
if( visible() ) {
const auto r = screen_rect();
display.scroll_set_area(r.top(), r.bottom());
display.scroll_set_position(0);
painter.fill_rectangle(
r,
background
);
} else {
display.scroll_disable();
}
*/
}
void Console::crlf() {
const auto line_height = style().font.line_height();
pos.x = 0;
pos.y += line_height;
const int32_t y_excess = pos.y + line_height - size().h;
if( y_excess > 0 ) {
display.scroll(-line_height);
pos.y -= y_excess;
const Rect dirty { 0, display.scroll_area_y(pos.y), size().w, line_height };
display.fill_rectangle(dirty, background);
}
}
} /* namespace ui */

View File

@ -0,0 +1,62 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __UI_CONSOLE_H__
#define __UI_CONSOLE_H__
#include "ui.hpp"
#include "ui_painter.hpp"
#include "ui_widget.hpp"
#include "lcd_ili9341.hpp"
#include <string>
namespace ui {
class Console : public Widget {
public:
constexpr Console(
const Rect parent_rect,
lcd::ILI9341& display
) : Widget { parent_rect },
display { display }
{
}
void write(const std::string message);
void writeln(const std::string message);
void paint(Painter& painter) override;
private:
static constexpr Color background { Color::black() };
static constexpr Color foreground { Color::white() };
Point pos { 0, 0 };
lcd::ILI9341& display;
void crlf();
};
} /* namespace ui */
#endif/*__UI_CONSOLE_H__*/

View File

@ -0,0 +1,135 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_debug.hpp"
#include "ch.h"
#include "radio.hpp"
namespace ui {
DebugMemoryView::DebugMemoryView(NavigationView& nav) {
add_children({ {
&text_title,
&text_label_m0_free,
&text_label_m0_free_value,
&text_label_m0_heap_fragmented_free,
&text_label_m0_heap_fragmented_free_value,
&text_label_m0_heap_fragments,
&text_label_m0_heap_fragments_value,
&button_done
} });
const auto m0_free = chCoreStatus();
text_label_m0_free_value.set(to_string_dec_uint(m0_free, 5));
size_t m0_fragmented_free_space = 0;
const auto m0_fragments = chHeapStatus(NULL, &m0_fragmented_free_space);
text_label_m0_heap_fragmented_free_value.set(to_string_dec_uint(m0_fragmented_free_space, 5));
text_label_m0_heap_fragments_value.set(to_string_dec_uint(m0_fragments, 5));
button_done.on_select = [&nav](Button&){ nav.pop(); };
}
void DebugMemoryView::focus() {
button_done.focus();
}
void DebugRFFC5072RegistersWidget::update() {
set_dirty();
}
void DebugRFFC5072RegistersWidget::paint(Painter& painter) {
draw_legend(painter);
const auto registers = radio::first_if.registers();
draw_values(painter, registers);
}
void DebugRFFC5072RegistersWidget::draw_legend(Painter& painter) {
for(size_t i=0; i<registers_count; i+=registers_per_row) {
const Point offset {
0, static_cast<Coord>((i / registers_per_row) * row_height)
};
const auto text = to_string_hex(i, legend_length);
painter.draw_string(
screen_pos() + offset,
style(),
text
);
}
}
void DebugRFFC5072RegistersWidget::draw_values(
Painter& painter,
const rffc507x::RegisterMap registers
) {
for(size_t i=0; i<registers_count; i++) {
const Point offset = {
static_cast<Coord>(legend_width + 8 + (i % registers_per_row) * (value_width + 8)),
static_cast<Coord>((i / registers_per_row) * row_height)
};
const uint16_t value = registers.w[i];
const auto text = to_string_hex(value, value_length);
painter.draw_string(
screen_pos() + offset,
style(),
text
);
}
}
DebugRFFC5072View::DebugRFFC5072View(NavigationView& nav) {
add_children({ {
&text_title,
&widget_registers,
&button_update,
&button_done,
} });
button_update.on_select = [this](Button&){
this->widget_registers.update();
};
button_done.on_select = [&nav](Button&){ nav.pop(); };
}
void DebugRFFC5072View::focus() {
button_done.focus();
}
DebugMenuView::DebugMenuView(NavigationView& nav) {
add_items<7>({ {
{ "Memory", [&nav](){ nav.push(new DebugMemoryView { nav }); } },
{ "Radio State", [&nav](){ nav.push(new NotImplementedView { nav }); } },
{ "SD Card", [&nav](){ nav.push(new NotImplementedView { nav }); } },
{ "RFFC5072", [&nav](){ nav.push(new DebugRFFC5072View { nav }); } },
{ "MAX2837", [&nav](){ nav.push(new NotImplementedView { nav }); } },
{ "Si5351C", [&nav](){ nav.push(new NotImplementedView { nav }); } },
{ "WM8731", [&nav](){ nav.push(new NotImplementedView { nav }); } },
} });
on_left = [&nav](){ nav.pop(); };
}
} /* namespace ui */

View File

@ -0,0 +1,152 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __UI_DEBUG_H__
#define __UI_DEBUG_H__
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_painter.hpp"
#include "ui_menu.hpp"
#include "ui_navigation.hpp"
#include "rffc507x.hpp"
namespace ui {
class DebugMemoryView : public View {
public:
DebugMemoryView(NavigationView& nav);
void focus() override;
private:
Text text_title {
{ 96, 96, 48, 16 },
"Memory",
};
Text text_label_m0_free {
{ 0, 128, 104, 16 },
"M0 Free Bytes",
};
Text text_label_m0_free_value {
{ 200, 128, 40, 16 },
};
Text text_label_m0_heap_fragmented_free {
{ 0, 144, 184, 16 },
"M0 Heap Fragmented Free",
};
Text text_label_m0_heap_fragmented_free_value {
{ 200, 144, 40, 16 },
};
Text text_label_m0_heap_fragments {
{ 0, 160, 136, 16 },
"M0 Heap Fragments",
};
Text text_label_m0_heap_fragments_value {
{ 200, 160, 40, 16 },
};
Button button_done {
{ 72, 192, 96, 24 },
"Done"
};
};
class DebugRFFC5072RegistersWidget : public Widget {
public:
constexpr DebugRFFC5072RegistersWidget(
Rect parent_rect
) : Widget { parent_rect }
{
}
void update();
void paint(Painter& painter) override;
private:
static constexpr size_t registers_count { 31 };
static constexpr size_t legend_length { 2 };
static constexpr Dim legend_width { legend_length * 8 };
static constexpr size_t value_length { 4 };
static constexpr Dim value_width { value_length * 8 };
static constexpr size_t registers_per_row { 4 };
static constexpr size_t registers_row_length {
(registers_per_row * (value_length + 1)) - 1
};
static constexpr Dim registers_row_width {
registers_row_length * 8
};
static constexpr size_t rows {
registers_count / registers_per_row
};
static constexpr Dim row_height { 16 };
void draw_legend(Painter& painter);
void draw_values(Painter& painter, const rffc507x::RegisterMap registers);
};
class DebugRFFC5072View : public View {
public:
DebugRFFC5072View(NavigationView& nav);
void focus() override;
private:
Text text_title {
{ 88, 16, 40, 16 },
"RFFC5072",
};
DebugRFFC5072RegistersWidget widget_registers {
{ 32, 48, 176, 128 }
};
Button button_update {
{ 16, 192, 96, 24 },
"Update"
};
Button button_done {
{ 128, 192, 96, 24 },
"Done"
};
};
class DebugMenuView : public MenuView {
public:
DebugMenuView(NavigationView& nav);
};
} /* namespace ui */
#endif/*__UI_DEBUG_H__*/

View File

@ -0,0 +1,138 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_font_fixed_8x16.hpp"
#include <cstdint>
namespace ui {
namespace font {
namespace {
const uint8_t fixed_8x16_glyph_data[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x24, 0x24, 0x24, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x48, 0x48, 0x48, 0xff, 0x24, 0x24, 0xff, 0x12, 0x12, 0x12, 0x00, 0x00, 0x00,
0x00, 0x00, 0x10, 0x78, 0x14, 0x14, 0x14, 0x18, 0x30, 0x50, 0x50, 0x50, 0x3c, 0x10, 0x00, 0x00,
0x00, 0x00, 0x00, 0x46, 0x29, 0x29, 0x19, 0x16, 0x68, 0x98, 0x94, 0x94, 0x62, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x18, 0x24, 0x24, 0x14, 0x88, 0x54, 0x72, 0x22, 0x62, 0x9c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x40, 0x20, 0x10, 0x10, 0x08, 0x08, 0x08, 0x08, 0x10, 0x10, 0x20, 0x40, 0x00,
0x00, 0x00, 0x00, 0x02, 0x04, 0x08, 0x08, 0x10, 0x10, 0x10, 0x10, 0x08, 0x08, 0x04, 0x02, 0x00,
0x00, 0x00, 0x00, 0x08, 0x2a, 0x1c, 0x2a, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0xfe, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x20, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x04, 0x02, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x18, 0x24, 0x42, 0x42, 0x5a, 0x5a, 0x42, 0x42, 0x24, 0x18, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0c, 0x0a, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x1e, 0x20, 0x20, 0x20, 0x20, 0x10, 0x08, 0x04, 0x02, 0x3e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x1e, 0x20, 0x20, 0x10, 0x0c, 0x10, 0x20, 0x20, 0x10, 0x0e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x30, 0x30, 0x28, 0x28, 0x24, 0x24, 0x22, 0x7e, 0x20, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x7c, 0x04, 0x04, 0x04, 0x3c, 0x40, 0x40, 0x40, 0x40, 0x3c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x38, 0x04, 0x02, 0x02, 0x3a, 0x46, 0x42, 0x42, 0x44, 0x38, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x7e, 0x40, 0x20, 0x20, 0x10, 0x10, 0x10, 0x08, 0x08, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x24, 0x18, 0x24, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x1c, 0x22, 0x42, 0x42, 0x62, 0x5c, 0x40, 0x40, 0x20, 0x1c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x20, 0x18, 0x04, 0x18, 0x20, 0x40, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x08, 0x30, 0x40, 0x30, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x38, 0x44, 0x40, 0x40, 0x30, 0x08, 0x08, 0x00, 0x08, 0x08, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x1c, 0x22, 0x41, 0x59, 0x55, 0x55, 0x55, 0x39, 0x01, 0x02, 0x3c, 0x00, 0x00,
0x00, 0x00, 0x00, 0x08, 0x14, 0x14, 0x14, 0x14, 0x22, 0x3e, 0x22, 0x41, 0x41, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x3e, 0x42, 0x42, 0x42, 0x3e, 0x42, 0x42, 0x42, 0x42, 0x3e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x38, 0x44, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x44, 0x38, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x1e, 0x22, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x22, 0x1e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x7e, 0x02, 0x02, 0x02, 0x7e, 0x02, 0x02, 0x02, 0x02, 0x7e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x7e, 0x02, 0x02, 0x02, 0x7e, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x38, 0x44, 0x02, 0x02, 0x02, 0x72, 0x42, 0x42, 0x44, 0x38, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x3e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x3e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x1e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x82, 0x42, 0x22, 0x12, 0x0a, 0x0e, 0x12, 0x22, 0x42, 0x82, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x7e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x42, 0x66, 0x66, 0x5a, 0x5a, 0x42, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x42, 0x46, 0x46, 0x4a, 0x4a, 0x52, 0x52, 0x62, 0x62, 0x42, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x18, 0x24, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x24, 0x18, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x3e, 0x42, 0x42, 0x3e, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x18, 0x24, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x24, 0x18, 0x10, 0x60, 0x00,
0x00, 0x00, 0x00, 0x3e, 0x42, 0x42, 0x42, 0x3e, 0x12, 0x22, 0x22, 0x42, 0x82, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x3c, 0x42, 0x02, 0x02, 0x0c, 0x30, 0x40, 0x40, 0x42, 0x3c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x7f, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x41, 0x41, 0x22, 0x22, 0x22, 0x14, 0x14, 0x14, 0x14, 0x08, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x41, 0x41, 0x41, 0x49, 0x49, 0x55, 0x55, 0x55, 0x22, 0x22, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x41, 0x22, 0x22, 0x14, 0x08, 0x08, 0x14, 0x22, 0x22, 0x41, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x41, 0x22, 0x22, 0x14, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x7e, 0x40, 0x20, 0x10, 0x10, 0x08, 0x08, 0x04, 0x02, 0x7e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x38, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x38, 0x00,
0x00, 0x00, 0x00, 0x02, 0x02, 0x04, 0x04, 0x08, 0x08, 0x10, 0x10, 0x20, 0x20, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x0e, 0x00,
0x00, 0x00, 0x00, 0x10, 0x28, 0x44, 0x44, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
0x00, 0x00, 0x00, 0x08, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x40, 0x7c, 0x42, 0x62, 0x5c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x1a, 0x26, 0x42, 0x42, 0x42, 0x26, 0x1a, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x04, 0x02, 0x02, 0x02, 0x04, 0x78, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x58, 0x64, 0x42, 0x42, 0x42, 0x64, 0x58, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x42, 0x7e, 0x02, 0x04, 0x78, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x70, 0x08, 0x08, 0x7e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x22, 0x22, 0x22, 0x1c, 0x02, 0x3e, 0x42, 0x42, 0x3c,
0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x3a, 0x46, 0x42, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x1c, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x1c, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x0e,
0x00, 0x00, 0x00, 0x02, 0x02, 0x02, 0x22, 0x12, 0x0a, 0x0e, 0x12, 0x22, 0x42, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x0e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x38, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x49, 0x49, 0x49, 0x49, 0x49, 0x49, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x46, 0x42, 0x42, 0x42, 0x42, 0x42, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x26, 0x42, 0x42, 0x42, 0x26, 0x1a, 0x02, 0x02, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x64, 0x42, 0x42, 0x42, 0x64, 0x58, 0x40, 0x40, 0x40,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x0c, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x02, 0x02, 0x3c, 0x40, 0x40, 0x3e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x08, 0x08, 0x3c, 0x08, 0x08, 0x08, 0x08, 0x08, 0x70, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x42, 0x62, 0x5c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x22, 0x22, 0x36, 0x14, 0x14, 0x08, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x41, 0x49, 0x55, 0x55, 0x22, 0x22, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x24, 0x18, 0x18, 0x18, 0x24, 0x42, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x22, 0x22, 0x14, 0x14, 0x14, 0x08, 0x08, 0x04, 0x03,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x20, 0x10, 0x08, 0x08, 0x04, 0x7e, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x10, 0x08, 0x08, 0x08, 0x08, 0x04, 0x08, 0x08, 0x08, 0x08, 0x08, 0x10, 0x00,
0x00, 0x00, 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x00,
0x00, 0x00, 0x00, 0x08, 0x10, 0x10, 0x10, 0x10, 0x20, 0x10, 0x10, 0x10, 0x10, 0x10, 0x08, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8e, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
}
const ui::Font fixed_8x16 {
8, 16,
fixed_8x16_glyph_data,
0x20, 95,
};
} /* namespace font */
} /* namespace ui */

View File

@ -0,0 +1,35 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __UI_FONT_FIXED_8X16_H__
#define __UI_FONT_FIXED_8X16_H__
#include "ui_text.hpp"
namespace ui {
namespace font {
extern const ui::Font fixed_8x16;
} /* namespace font */
} /* namspace ui */
#endif/*__UI_FONT_FIXED_8X16_H__*/

View File

@ -0,0 +1,170 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_menu.hpp"
namespace ui {
/* MenuViewItem **********************************************************/
void MenuItemView::select() {
if( item.on_select ) {
item.on_select();
}
}
void MenuItemView::highlight() {
set_highlight(true);
}
void MenuItemView::unhighlight() {
set_highlight(false);
}
void MenuItemView::paint(Painter& painter) {
const auto r = screen_rect();
const auto paint_style = (flags.highlighted && parent()->has_focus()) ? style().invert() : style();
const auto font_height = paint_style.font.line_height();
painter.fill_rectangle(
r,
paint_style.background
);
painter.draw_string(
{ static_cast<Coord>(r.pos.x + 8), static_cast<Coord>(r.pos.y + (r.size.h - font_height) / 2) },
paint_style,
item.text
);
}
void MenuItemView::set_highlight(const bool value) {
flags.highlighted = value;
set_dirty();
}
/* MenuView **************************************************************/
MenuView::~MenuView() {
/* TODO: Double-check this */
for(auto child : children_) {
delete child;
}
}
void MenuView::add_item(const MenuItem item) {
add_child(new MenuItemView { item });
}
void MenuView::set_parent_rect(const Rect new_parent_rect) {
View::set_parent_rect(new_parent_rect);
constexpr size_t item_height = 24;
size_t i = 0;
for(auto child : children_) {
child->set_parent_rect({
{ 0, static_cast<ui::Coord>(i * item_height) },
{ size().w, item_height }
});
i++;
}
}
MenuItemView* MenuView::item_view(size_t index) const {
/* TODO: Terrible cast! Take it as a sign I must be doing something
* shamefully wrong here, right?
*/
return static_cast<MenuItemView*>(children_[index]);
}
size_t MenuView::highlighted() const {
return highlighted_;
}
bool MenuView::set_highlighted(const size_t new_value) {
if( new_value >= children_.size() ) {
return false;
}
item_view(highlighted())->unhighlight();
highlighted_ = new_value;
item_view(highlighted())->highlight();
return true;
}
void MenuView::on_focus() {
item_view(highlighted())->highlight();
}
void MenuView::on_blur() {
item_view(highlighted())->unhighlight();
}
bool MenuView::on_key(const KeyEvent key) {
switch(key) {
case KeyEvent::Up:
return set_highlighted(highlighted() - 1);
case KeyEvent::Down:
return set_highlighted(highlighted() + 1);
case KeyEvent::Select:
case KeyEvent::Right:
item_view(highlighted())->select();
return true;
case KeyEvent::Left:
if( on_left ) {
on_left();
}
return true;
default:
return false;
}
}
bool MenuView::on_encoder(const EncoderEvent event) {
set_highlighted(highlighted() + event);
return true;
}
/* TODO: This could be handled by default behavior, if the UI stack were to
* transmit consumable events from the top of the hit-stack down, and each
* MenuItem could respond to a touch and update its parent MenuView.
*/
/*
bool MenuView::on_touch(const TouchEvent event) {
size_t i = 0;
for(const auto child : children_) {
if( child->screen_rect().contains(event.point) ) {
return set_highlighted(i);
}
i++;
}
return false;
}
*/
} /* namespace ui */

View File

@ -0,0 +1,98 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __UI_MENU_H__
#define __UI_MENU_H__
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_painter.hpp"
#include <cstddef>
#include <string>
#include <functional>
namespace ui {
struct MenuItem {
std::string text;
std::function<void(void)> on_select;
};
class MenuItemView : public Widget {
public:
MenuItemView(
MenuItem item
) : item(item)
{
}
void paint(Painter& painter) override;
void select();
void highlight();
void unhighlight();
private:
const MenuItem item;
void set_highlight(const bool value);
};
class MenuView : public View {
public:
std::function<void(void)> on_left;
MenuView() {
flags.focusable = true;
}
~MenuView();
void add_item(const MenuItem item);
template<size_t N>
void add_items(const std::array<MenuItem, N> items) {
for(const auto& item : items) {
add_item(item);
}
}
void set_parent_rect(const Rect new_parent_rect) override;
MenuItemView* item_view(size_t index) const;
size_t highlighted() const;
bool set_highlighted(const size_t new_value);
void on_focus() override;
void on_blur() override;
bool on_key(const KeyEvent event) override;
bool on_encoder(const EncoderEvent event) override;
//bool on_touch(const TouchEvent event) override;
private:
size_t highlighted_ { 0 };
};
} /* namespace ui */
#endif/*__UI_MENU_H__*/

View File

@ -0,0 +1,194 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_navigation.hpp"
#include "receiver_model.hpp"
#include "ui_setup.hpp"
#include "ui_debug.hpp"
#include "ui_receiver.hpp"
#include "hackrf_hal.hpp"
#include "hackrf_gpio.hpp"
using namespace hackrf::one;
extern ReceiverModel receiver_model;
namespace ui {
/* SystemStatusView ******************************************************/
SystemStatusView::SystemStatusView() {
add_children({ {
&portapack,
//&text_app_fifo_n,
//&text_baseband_fifo_n,
&text_ticks,
&rssi,
&channel,
&audio,
} });
}
void SystemStatusView::on_show() {
context().message_map[Message::ID::BasebandStatistics] = [this](const Message* const p) {
this->on_statistics_update(static_cast<const BasebandStatisticsMessage*>(p)->statistics);
};
}
void SystemStatusView::on_hide() {
context().message_map[Message::ID::BasebandStatistics] = nullptr;
}
static std::string ticks_to_percent_string(const uint32_t ticks) {
const uint32_t percent_x100 = ticks / (base_m4_clk_f / 10000);
return
to_string_dec_uint(percent_x100 / 100, 3) + "." +
to_string_dec_uint(percent_x100 % 100, 2, '0') + "%";
}
void SystemStatusView::on_statistics_update(const BasebandStatistics& statistics) {
led_tx.write(statistics.saturation);
portapack.set(ticks_to_percent_string(statistics.baseband_ticks));
text_ticks.set(ticks_to_percent_string(statistics.idle_ticks));
}
/* Navigation ************************************************************/
NavigationView::NavigationView()
{
}
void NavigationView::push(View* new_view) {
// TODO: Trap nullptr?
// TODO: Trap push of object already on stack?
view_stack.push_back(new_view);
set_view(new_view);
}
void NavigationView::pop() {
// Can't pop last item from stack.
if( view_stack.size() > 1 ) {
const auto old_view = view_stack.back();
view_stack.pop_back();
const auto new_view = view_stack.back();
set_view(new_view);
delete old_view;
}
}
Widget* NavigationView::view() const {
return children_.empty() ? nullptr : children_[0];
}
void NavigationView::set_view(Widget* const new_view) {
const auto old_view = view();
if( old_view ) {
remove_child(old_view);
}
// TODO: Allow new_view == nullptr?!
if( new_view ) {
add_child(new_view);
new_view->set_parent_rect({ {0, 0}, size() });
focus();
}
set_dirty();
}
void NavigationView::focus() {
if( view() ) {
view()->focus();
}
}
/* SystemMenuView ********************************************************/
SystemMenuView::SystemMenuView(NavigationView& nav) {
add_items<6>({ {
{ "Receiver", [&nav](){ nav.push(new ReceiverView { nav, receiver_model }); } },
{ "Capture", [&nav](){ nav.push(new NotImplementedView { nav }); } },
{ "Analyze", [&nav](){ nav.push(new NotImplementedView { nav }); } },
{ "Setup", [&nav](){ nav.push(new SetupMenuView { nav }); } },
{ "About", [&nav](){ nav.push(new AboutView { nav }); } },
{ "Debug", [&nav](){ nav.push(new DebugMenuView { nav }); } },
} });
}
/* SystemView ************************************************************/
static constexpr ui::Style style_default {
.font = ui::font::fixed_8x16,
.background = ui::Color::black(),
.foreground = ui::Color::white(),
};
SystemView::SystemView(
Context& context,
const Rect parent_rect
) : View { parent_rect },
context_ { context }
{
style_ = &style_default;
constexpr ui::Dim status_view_height = 16;
add_child(&status_view);
status_view.set_parent_rect({
{ 0, 0 },
{ parent_rect.width(), status_view_height }
});
add_child(&navigation_view);
navigation_view.set_parent_rect({
{ 0, status_view_height },
{ parent_rect.width(), static_cast<ui::Dim>(parent_rect.height() - status_view_height) }
});
// Initial view.
// TODO: Restore from non-volatile memory?
navigation_view.push(new SystemMenuView { navigation_view });
}
Context& SystemView::context() const {
return context_;
}
/* NotImplementedView ****************************************************/
NotImplementedView::NotImplementedView(NavigationView& nav) {
button_done.on_select = [&nav](Button&){
nav.pop();
};
add_children({ {
&text_title,
&button_done,
} });
}
void NotImplementedView::focus() {
button_done.focus();
}
} /* namespace ui */

View File

@ -0,0 +1,140 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __UI_NAVIGATION_H__
#define __UI_NAVIGATION_H__
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_focus.hpp"
#include "ui_menu.hpp"
#include "ui_rssi.hpp"
#include "ui_channel.hpp"
#include "ui_audio.hpp"
#include <vector>
namespace ui {
class SystemStatusView : public View {
public:
SystemStatusView();
void on_show() override;
void on_hide() override;
private:
Text portapack {
{ 0, 0, 9 * 8, 1 * 16 },
"PortaPack",
};
/*
Text text_app_fifo_n {
{ 12 * 8, 0, 3 * 8, 1 * 16 },
"---",
};
Text text_baseband_fifo_n {
{ 16 * 8, 0, 3 * 8, 1 * 16 },
"---",
};
*/
Text text_ticks {
{ 9 * 8, 0, 7 * 8, 1 * 16 },
"",
};
RSSI rssi {
{ 19 * 8, 0, 11 * 8, 4 },
};
Channel channel {
{ 19 * 8, 5, 11 * 8, 4 },
};
Audio audio {
{ 19 * 8, 10, 11 * 8, 4 },
};
void on_statistics_update(const BasebandStatistics& statistics);
};
class NavigationView : public View {
public:
NavigationView();
NavigationView(const NavigationView&) = delete;
NavigationView(NavigationView&&) = delete;
void push(View* new_view);
void pop();
void focus() override;
private:
std::vector<View*> view_stack;
Widget* view() const;
void set_view(Widget* const new_view);
};
class SystemMenuView : public MenuView {
public:
SystemMenuView(NavigationView& nav);
};
class SystemView : public View {
public:
SystemView(
Context& context,
const Rect parent_rect
);
Context& context() const override;
private:
SystemStatusView status_view;
NavigationView navigation_view;
Context& context_;
};
class NotImplementedView : public View {
public:
NotImplementedView(NavigationView& nav);
void focus() override;
private:
Text text_title {
{ 5 * 8, 7 * 16, 19 * 8, 16 },
"Not Yet Implemented"
};
Button button_done {
{ 10 * 8, 13 * 16, 10 * 8, 24 },
"Bummer",
};
};
} /* namespace ui */
#endif/*__UI_NAVIGATION_H__*/

View File

@ -0,0 +1,557 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_receiver.hpp"
#include "hackrf_gpio.hpp"
using namespace hackrf::one;
// TODO: Nasty. Put this in an #include somewhere, or a shared system state
// object?
#include "wm8731.hpp"
extern wolfson::wm8731::WM8731 audio_codec;
namespace ui {
/* BasebandBandwidthField ************************************************/
BasebandBandwidthField::BasebandBandwidthField(
Point parent_pos
) : OptionsField {
parent_pos,
4,
{
{ " 1M8", 1750000 },
{ " 2M5", 2500000 },
{ " 3M5", 3500000 },
{ " 5M ", 5000000 },
{ " 5M5", 5500000 },
{ " 6M ", 6000000 },
{ " 7M ", 7000000 },
{ " 8M ", 8000000 },
{ " 9M ", 9000000 },
{ "10M ", 10000000 },
{ "12M ", 12000000 },
{ "14M ", 14000000 },
{ "15M ", 15000000 },
{ "20M ", 20000000 },
{ "24M ", 24000000 },
{ "28M ", 28000000 },
}
}
{
}
/* FrequencyField ********************************************************/
FrequencyField::FrequencyField(
const Point parent_pos
) : Widget { { parent_pos, { 8 * 10, 16 } } },
length_ { 11 },
range(rf::tuning_range)
{
flags.focusable = true;
}
rf::Frequency FrequencyField::value() const {
return value_;
}
void FrequencyField::set_value(rf::Frequency new_value) {
new_value = clamp_value(new_value);
if( new_value != value_ ) {
value_ = new_value;
if( on_change ) {
on_change(value_);
}
set_dirty();
}
}
void FrequencyField::set_step(rf::Frequency new_value) {
step = new_value;
// TODO: Quantize current frequency to a step of the new size?
}
void FrequencyField::paint(Painter& painter) {
const auto mhz = to_string_dec_int(value_ / 1000000, 4);
const auto hz100 = to_string_dec_int((value_ / 100) % 10000, 4, '0');
const auto paint_style = has_focus() ? style().invert() : style();
painter.draw_string(
screen_pos(),
paint_style,
mhz
);
painter.draw_string(
screen_pos() + Point { 4 * 8, 0 },
paint_style,
"."
);
painter.draw_string(
screen_pos() + Point { 5 * 8, 0 },
paint_style,
hz100
);
}
bool FrequencyField::on_key(const ui::KeyEvent event) {
if( event == ui::KeyEvent::Select ) {
if( on_edit ) {
on_edit();
return true;
}
}
return false;
}
/*
bool FrequencyField::on_key(const ui::KeyEvent event) override {
if( event == ui::KeyEvent::Select ) {
// NOTE: For testing sampling rate / decimation combinations
turbo = !turbo;
if( turbo ) {
clock_manager.set_sampling_frequency(18432000);
radio::set_baseband_decimation_by(6);
} else {
clock_manager.set_sampling_frequency(12288000);
radio::set_baseband_decimation_by(4);
}
return true;
}
return false;
}
*/
bool FrequencyField::on_encoder(const EncoderEvent delta) {
set_value(value() + (delta * step));
return true;
}
bool FrequencyField::on_touch(const TouchEvent event) {
if( event.type == TouchEvent::Type::Start ) {
focus();
}
return true;
}
void FrequencyField::on_focus() {
if( on_show_options ) {
on_show_options();
}
}
rf::Frequency FrequencyField::clamp_value(rf::Frequency value) {
if( value > range.max ) {
value = range.max;
}
if( value < range.min ) {
value = range.min;
}
return value;
}
/* FrequencyKeypadView ***************************************************/
FrequencyKeypadView::FrequencyKeypadView(
NavigationView& nav,
const rf::Frequency value
) {
add_child(&text_value);
const auto button_fn = [this](Button& button) {
this->on_button(button);
};
const char* const key_caps = "123456789<0.";
size_t n = 0;
for(auto& button : buttons) {
add_child(&button);
const std::string label {
key_caps[n]
};
button.on_select = button_fn;
button.set_parent_rect({
static_cast<Coord>((n % 3) * button_w),
static_cast<Coord>((n / 3) * button_h + button_h),
button_w, button_h
});
button.set_text(label);
n++;
}
add_child(&button_close);
button_close.on_select = [this, &nav](Button&) {
if( on_changed ) {
on_changed(this->value());
}
nav.pop();
};
set_value(value);
}
void FrequencyKeypadView::focus() {
button_close.focus();
}
rf::Frequency FrequencyKeypadView::value() const {
return mhz.as_int() * 1000000ULL + submhz.as_int() * submhz_base;
}
void FrequencyKeypadView::set_value(const rf::Frequency new_value) {
mhz.set(new_value / 1000000);
mhz.remove_leading_zeros();
submhz.set((new_value % 1000000) / submhz_base);
submhz.remove_trailing_zeros();
update_text();
}
void FrequencyKeypadView::on_button(Button& button) {
const auto s = button.text();
if( s == "." ) {
field_toggle();
} else if( s == "<" ) {
digit_delete();
} else {
digit_add(s[0]);
}
update_text();
}
void FrequencyKeypadView::digit_add(const char c) {
if( state == State::DigitMHz ) {
if( clear_field_if_digits_entered ) {
mhz.clear();
}
mhz.add_digit(c);
} else {
submhz.add_digit(c);
}
clear_field_if_digits_entered = false;
}
void FrequencyKeypadView::digit_delete() {
if( state == State::DigitMHz ) {
mhz.delete_digit();
} else {
submhz.delete_digit();
}
}
void FrequencyKeypadView::field_toggle() {
if( state == State::DigitMHz ) {
state = State::DigitSubMHz;
submhz.clear();
} else {
state = State::DigitMHz;
clear_field_if_digits_entered = true;
}
}
void FrequencyKeypadView::update_text() {
const auto s = mhz.as_string() + "." + submhz.as_string();
text_value.set(s);
}
/* FrequencyOptionsView **************************************************/
FrequencyOptionsView::FrequencyOptionsView(
const Rect parent_rect,
const Style* const style
) : View { parent_rect }
{
set_style(style);
options_step.on_change = [this](size_t n, OptionsField::value_t v) {
(void)n;
this->on_step_changed(v);
};
add_children({ {
&text_step,
&options_step,
} });
}
void FrequencyOptionsView::set_step(rf::Frequency f) {
options_step.set_by_value(f);
}
void FrequencyOptionsView::on_step_changed(rf::Frequency v) {
if( on_change_step ) {
on_change_step(v);
}
}
/* RadioGainOptionsView **************************************************/
RadioGainOptionsView::RadioGainOptionsView(
const Rect parent_rect,
const Style* const style
) : View { parent_rect }
{
set_style(style);
add_children({ {
&label_rf_amp,
&field_rf_amp,
//&label_agc,
//&field_agc
} });
field_rf_amp.on_change = [this](int32_t v) {
this->on_rf_amp_changed(v);
};
/*
field_agc.set_value(receiver_model.agc());
field_agc.on_change = [this](int32_t v) {
this->on_agc_changed(v);
};
*/
}
void RadioGainOptionsView::set_rf_amp(int32_t v_db) {
field_rf_amp.set_value(v_db);
}
void RadioGainOptionsView::on_rf_amp_changed(bool enable) {
if( on_change_rf_amp ) {
on_change_rf_amp(enable);
}
}
/*
void RadioGainOptionsView::on_agc_changed(bool v) {
receiver_model.set_agc(v);
}
*/
/* LNAGainField **********************************************************/
LNAGainField::LNAGainField(
Point parent_pos
) : NumberField {
{ parent_pos }, 2,
{ max2837::lna::gain_db_min, max2837::lna::gain_db_max },
max2837::lna::gain_db_step,
' ',
}
{
}
void LNAGainField::on_focus() {
//Widget::on_focus();
if( on_show_options ) {
on_show_options();
}
}
/* ReceiverView **********************************************************/
ReceiverView::ReceiverView(
NavigationView& nav,
ReceiverModel& receiver_model
) : receiver_model { receiver_model }
{
add_children({ {
&button_done,
&field_frequency,
&field_lna,
//&options_baseband_bandwidth,
&field_vga,
&options_modulation,
//&options_baseband_oversampling,
&field_vregmode,
&field_volume,
&view_frequency_options,
&view_rf_gain_options,
&waterfall,
} });
button_done.on_select = [&nav](Button&){
nav.pop();
};
field_frequency.set_value(receiver_model.tuning_frequency());
field_frequency.set_step(receiver_model.frequency_step());
field_frequency.on_change = [this](rf::Frequency f) {
this->on_tuning_frequency_changed(f);
};
field_frequency.on_edit = [this, &nav]() {
// TODO: Provide separate modal method/scheme?
auto new_view = new FrequencyKeypadView { nav, this->receiver_model.tuning_frequency() };
new_view->on_changed = [this](rf::Frequency f) {
this->on_tuning_frequency_changed(f);
this->field_frequency.set_value(f);
};
nav.push(new_view);
};
field_frequency.on_show_options = [this]() {
this->on_show_options_frequency();
};
field_lna.set_value(receiver_model.lna());
field_lna.on_change = [this](int32_t v) {
this->on_lna_changed(v);
};
field_lna.on_show_options = [this]() {
this->on_show_options_rf_gain();
};
/*
options_baseband_bandwidth.set_by_value(receiver_model.baseband_bandwidth());
options_baseband_bandwidth.on_change = [this](size_t n, OptionsField::value_t v) {
(void)n;
this->on_baseband_bandwidth_changed(v);
};
*/
field_vga.set_value(receiver_model.vga());
field_vga.on_change = [this](int32_t v_db) {
this->on_vga_changed(v_db);
};
options_modulation.set_by_value(receiver_model.modulation());
options_modulation.on_change = [this](size_t n, OptionsField::value_t v) {
(void)n;
this->on_modulation_changed(v);
};
/*
options_baseband_oversampling.set_by_value(receiver_model.baseband_oversampling());
options_baseband_oversampling.on_change = [this](size_t n, OptionsField::value_t v) {
(void)n;
this->on_baseband_oversampling_changed(v);
};
*/
field_vregmode.set_value(0);
field_vregmode.on_change = [](int32_t v) {
gpio_vregmode.write(v);
gpio_vregmode.output();
led_rx.write(v);
};
field_volume.set_value((receiver_model.headphone_volume() - wolfson::wm8731::headphone_gain_range.max).decibel() + 99);
field_volume.on_change = [this](int32_t v) {
this->on_headphone_volume_changed(v);
};
view_frequency_options.hidden(true);
view_frequency_options.set_step(receiver_model.frequency_step());
view_frequency_options.on_change_step = [this](rf::Frequency f) {
this->on_frequency_step_changed(f);
};
view_rf_gain_options.hidden(true);
view_rf_gain_options.set_rf_amp(receiver_model.rf_amp());
view_rf_gain_options.on_change_rf_amp = [this](bool enable) {
this->on_rf_amp_changed(enable);
};
receiver_model.enable();
}
ReceiverView::~ReceiverView() {
// TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do
// both?
audio_codec.headphone_mute();
receiver_model.disable();
}
void ReceiverView::set_parent_rect(const Rect new_parent_rect) {
const ui::Dim header_height = 2 * 16;
waterfall.set_parent_rect({
0, header_height,
new_parent_rect.width(), static_cast<ui::Dim>(new_parent_rect.height() - header_height)
});
}
void ReceiverView::focus() {
button_done.focus();
}
void ReceiverView::on_tuning_frequency_changed(rf::Frequency f) {
receiver_model.set_tuning_frequency(f);
}
void ReceiverView::on_baseband_bandwidth_changed(uint32_t bandwidth_hz) {
receiver_model.set_baseband_bandwidth(bandwidth_hz);
}
void ReceiverView::on_rf_amp_changed(bool v) {
receiver_model.set_rf_amp(v);
}
void ReceiverView::on_lna_changed(int32_t v_db) {
receiver_model.set_lna(v_db);
}
void ReceiverView::on_vga_changed(int32_t v_db) {
receiver_model.set_vga(v_db);
}
void ReceiverView::on_modulation_changed(int32_t modulation) {
if( modulation == 3 ) {
/* TODO: This is TERRIBLE!!! */
receiver_model.set_sampling_rate(2457600);
} else {
receiver_model.set_sampling_rate(3072000);
}
receiver_model.set_modulation(modulation);
}
void ReceiverView::on_show_options_frequency() {
view_rf_gain_options.hidden(true);
field_lna.set_style(nullptr);
view_frequency_options.hidden(false);
field_frequency.set_style(&view_frequency_options.style());
}
void ReceiverView::on_show_options_rf_gain() {
view_frequency_options.hidden(true);
field_frequency.set_style(nullptr);
view_rf_gain_options.hidden(false);
field_lna.set_style(&view_frequency_options.style());
}
void ReceiverView::on_frequency_step_changed(rf::Frequency f) {
receiver_model.set_frequency_step(f);
field_frequency.set_step(f);
}
void ReceiverView::on_headphone_volume_changed(int32_t v) {
const auto new_volume = volume_t::decibel(v - 99) + wolfson::wm8731::headphone_gain_range.max;
receiver_model.set_headphone_volume(new_volume);
}
// void ReceiverView::on_baseband_oversampling_changed(int32_t v) {
// receiver_model.set_baseband_oversampling(v);
// }
} /* namespace ui */

View File

@ -0,0 +1,443 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __UI_RECEIVER_H__
#define __UI_RECEIVER_H__
#include "ui.hpp"
#include "ui_font_fixed_8x16.hpp"
#include "ui_navigation.hpp"
#include "ui_painter.hpp"
#include "ui_widget.hpp"
#include "ui_spectrum.hpp"
#include "utility.hpp"
#include "max2837.hpp"
#include "rf_path.hpp"
#include "volume.hpp"
#include "wm8731.hpp"
#include "receiver_model.hpp"
#include <cstddef>
#include <cstdint>
#include <algorithm>
#include <functional>
namespace ui {
class BasebandBandwidthField : public OptionsField {
public:
BasebandBandwidthField(Point parent_pos);
};
class FrequencyField : public Widget {
public:
std::function<void(rf::Frequency)> on_change;
std::function<void(void)> on_edit;
std::function<void(void)> on_show_options;
using range_t = rf::FrequencyRange;
FrequencyField(const Point parent_pos);
rf::Frequency value() const;
void set_value(rf::Frequency new_value);
void set_step(rf::Frequency new_value);
void paint(Painter& painter) override;
bool on_key(const ui::KeyEvent event) override;
bool on_encoder(const EncoderEvent delta) override;
bool on_touch(const TouchEvent event) override;
void on_focus() override;
private:
const size_t length_;
const range_t range;
rf::Frequency value_;
rf::Frequency step { 25000 };
//bool turbo { false };
rf::Frequency clamp_value(rf::Frequency value);
};
template<size_t N>
class FieldString {
public:
enum Justify {
Right,
Left,
};
constexpr FieldString(
Justify justify
) : justify { justify }
{
}
uint32_t as_int() const {
uint32_t value = 0;
for(const auto c : s) {
const uint_fast8_t digit = (c == ' ') ? 0 : c - '0';
value = (value * 10) + digit;
}
return value;
}
void set(uint32_t value) {
std::generate(s.rbegin(), s.rend(), [&value]() {
const char digit = (value % 10) + '0';
value /= 10;
return digit;
});
}
void clear() {
s.fill(' ');
}
void add_digit(const char c) {
/*
if( justify == Justify::Right ) {
push_right(c);
} else {
insert_right(c);
}
*/
insert_right(c);
}
void delete_digit() {
if( justify == Justify::Right ) {
shift_right();
s.front() = ' ';
} else {
auto first_digit = std::find_if(s.rbegin(), s.rend(), [](const char& a) {
return a != ' ';
});
if( first_digit != s.rend() ) {
*first_digit = ' ';
}
}
}
std::string as_string() const {
return { s.data(), s.size() };
}
void remove_leading_zeros() {
remove_zeros(s.begin(), s.end());
}
void remove_trailing_zeros() {
remove_zeros(s.rbegin(), s.rend());
}
private:
using array_type = std::array<char, N>;
array_type s;
Justify justify;
template<typename Iterator>
void remove_zeros(Iterator begin, Iterator end) {
const auto first_significant_digit =
std::find_if(begin, end, [](const char& a) {
return a != '0';
});
std::fill(begin, first_significant_digit, ' ');
}
void insert_right(const char c) {
auto insert_point = s.end() - 1;
if( justify == Justify::Left ) {
insert_point = std::find_if(s.begin(), s.end(), [](const char& a) {
return a == ' ';
});
}
if( *insert_point != ' ' ) {
insert_point = shift_left();
}
*insert_point = c;
}
typename array_type::iterator shift_left() {
return std::move(s.begin() + 1, s.end(), s.begin());
}
typename array_type::iterator shift_right() {
return std::move_backward(s.begin(), s.end() - 1, s.end());
}
};
class FrequencyKeypadView : public View {
public:
std::function<void(rf::Frequency)> on_changed;
FrequencyKeypadView(
NavigationView& nav,
const rf::Frequency value
);
void focus() override;
rf::Frequency value() const;
void set_value(const rf::Frequency new_value);
private:
static constexpr size_t button_w = 240 / 3;
static constexpr size_t button_h = 48;
static constexpr size_t mhz_digits = 4;
static constexpr size_t submhz_digits = 4;
static constexpr size_t mhz_mod = pow(10, mhz_digits);
static constexpr size_t submhz_base = pow(10, 6 - submhz_digits);
//static constexpr size_t submhz_mod = pow(10, submhz_digits);
static constexpr size_t text_digits = mhz_digits + 1 + submhz_digits;
Text text_value {
{ 0, 0, text_digits * button_w, button_h }
};
std::array<Button, 12> buttons;
Button button_close {
{ 0, button_h * 4 + button_h, button_w * 3, button_h },
"Done"
};
/* TODO: Template arg required in enum?! */
FieldString<mhz_digits> mhz { FieldString<4>::Justify::Right };
FieldString<submhz_digits> submhz { FieldString<4>::Justify::Left };
enum State {
DigitMHz,
DigitSubMHz
};
State state { DigitMHz };
bool clear_field_if_digits_entered { true };
void on_button(Button& button);
void digit_add(const char c);
void digit_delete();
void field_toggle();
void update_text();
};
class FrequencyOptionsView : public View {
public:
std::function<void(rf::Frequency)> on_change_step;
FrequencyOptionsView(const Rect parent_rect, const Style* const style);
void set_step(rf::Frequency f);
private:
Text text_step {
{ 0 * 8, 0 * 16, 4 * 8, 1 * 16 },
"Step"
};
OptionsField options_step {
{ 5 * 8, 0 * 16 },
5,
{
{ " 100", 100 },
{ " 1k ", 1000 },
{ " 10k ", 10000 },
{ " 12k5", 12500 },
{ " 25k ", 25000 },
{ "100k ", 100000 },
{ " 1M ", 1000000 },
{ " 10M ", 10000000 },
}
};
void on_step_changed(rf::Frequency v);
};
class RadioGainOptionsView : public View {
public:
std::function<void(bool)> on_change_rf_amp;
RadioGainOptionsView(const Rect parent_rect, const Style* const style);
void set_rf_amp(int32_t v_db);
private:
Text label_rf_amp {
{ 0 * 8, 0 * 16, 3 * 8, 1 * 16 },
"Amp"
};
NumberField field_rf_amp {
{ 4 * 8, 0 * 16},
1,
{ 0, 1 },
1,
' ',
};
/*
Text label_agc {
{ 6 * 8, 0 * 16, 3 * 8, 1 * 16 },
"AGC"
};
NumberField field_agc {
{ 10 * 8, 0 * 16},
1,
{ 0, 1 }
};
*/
void on_rf_amp_changed(bool enable);
};
class LNAGainField : public NumberField {
public:
std::function<void(void)> on_show_options;
LNAGainField(Point parent_pos);
void on_focus() override;
};
constexpr Style style_options_group {
.font = font::fixed_8x16,
.background = Color::blue(),
.foreground = Color::white(),
};
class ReceiverView : public View {
public:
ReceiverView(NavigationView& nav, ReceiverModel& receiver_model);
~ReceiverView();
void set_parent_rect(const Rect new_parent_rect) override;
void focus() override;
private:
ReceiverModel& receiver_model;
Button button_done {
{ 0 * 8, 0 * 16, 3 * 8, 16 },
" < ",
};
FrequencyField field_frequency {
{ 3 * 8, 0 * 16 },
};
LNAGainField field_lna {
{ 13 * 8, 0 * 16 }
};
/*
BasebandBandwidthField options_baseband_bandwidth {
{ 15 * 8, 0 * 16 },
};
*/
NumberField field_vga {
{ 16 * 8, 0 * 16},
2,
{ max2837::vga::gain_db_min, max2837::vga::gain_db_max },
max2837::vga::gain_db_step,
' ',
};
OptionsField options_modulation {
{ 19 * 8, 0 * 16 },
4,
{
// TODO: Put ordinals in here...
{ " AM ", 0 },
{ "NFM ", 1 },
{ "WFM ", 2 },
{ "FSK ", 3 },
}
};
/*
OptionsField options_baseband_oversampling {
{ 24 * 8, 0 * 16 },
1,
{
{ "4", 4 },
{ "6", 6 },
{ "8", 8 },
}
};
*/
NumberField field_vregmode {
{ 24 * 8, 0 * 16 },
1,
{ 0, 1 },
1,
' ',
};
NumberField field_volume {
{ 28 * 8, 0 * 16 },
2,
{ 0, 99 },
1,
' ',
};
FrequencyOptionsView view_frequency_options {
{ 0 * 8, 1 * 16, 30 * 8, 1 * 16 },
&style_options_group
};
RadioGainOptionsView view_rf_gain_options {
{ 0 * 8, 1 * 16, 30 * 8, 1 * 16 },
&style_options_group
};
spectrum::WaterfallWidget waterfall;
void on_tuning_frequency_changed(rf::Frequency f);
void on_baseband_bandwidth_changed(uint32_t bandwidth_hz);
void on_rf_amp_changed(bool v);
void on_lna_changed(int32_t v_db);
void on_vga_changed(int32_t v_db);
void on_modulation_changed(int32_t modulation);
void on_show_options_frequency();
void on_show_options_rf_gain();
void on_frequency_step_changed(rf::Frequency f);
void on_headphone_volume_changed(int32_t v);
// void on_baseband_oversampling_changed(int32_t v);
void on_edit_frequency();
};
} /* namespace ui */
#endif/*__UI_RECEIVER_H__*/

View File

@ -0,0 +1,106 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_rssi.hpp"
#include <algorithm>
namespace ui {
void RSSI::on_show() {
context().message_map[Message::ID::RSSIStatistics] = [this](const Message* const p) {
this->on_statistics_update(static_cast<const RSSIStatisticsMessage*>(p)->statistics);
};
}
void RSSI::on_hide() {
context().message_map[Message::ID::RSSIStatistics] = nullptr;
}
void RSSI::paint(Painter& painter) {
const auto r = screen_rect();
/*
constexpr int32_t rssi_min = 0.# * 256 / 3.3;
constexpr int32_t rssi_max = 2.5 * 256 / 3.3;
// (23 - 194) / 2
*/
/* TODO: Clip maximum */
constexpr int32_t raw_min = 23;
const int32_t x_0 = 0;
const int32_t x_min = std::max(x_0, (min_ - raw_min) / 2);
const int32_t x_avg = std::max(x_min, (avg_ - raw_min) / 2);
const int32_t x_max = std::max(x_avg + 1, (max_ - raw_min) / 2);
const int32_t x_lim = r.width();
const Rect r0 {
static_cast<ui::Coord>(r.left() + x_0), r.top(),
static_cast<ui::Dim>(x_min - x_0), r.height()
};
painter.fill_rectangle(
r0,
Color::blue()
);
const Rect r1 {
static_cast<ui::Coord>(r.left() + x_min), r.top(),
static_cast<ui::Dim>(x_avg - x_min), r.height()
};
painter.fill_rectangle(
r1,
Color::red()
);
const Rect r2 {
static_cast<ui::Coord>(r.left() + x_avg), r.top(),
1, r.height()
};
painter.fill_rectangle(
r2,
Color::white()
);
const Rect r3 {
static_cast<ui::Coord>(r.left() + x_avg + 1), r.top(),
static_cast<ui::Dim>(x_max - (x_avg + 1)), r.height()
};
painter.fill_rectangle(
r3,
Color::red()
);
const Rect r4 {
static_cast<ui::Coord>(r.left() + x_max), r.top(),
static_cast<ui::Dim>(x_lim - x_max), r.height()
};
painter.fill_rectangle(
r4,
Color::black()
);
}
void RSSI::on_statistics_update(const RSSIStatistics& statistics) {
min_ = statistics.min;
avg_ = statistics.accumulator / statistics.count;
max_ = statistics.max;
set_dirty();
}
} /* namespace ui */

View File

@ -0,0 +1,59 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __UI_RSSI_H__
#define __UI_RSSI_H__
#include "ui.hpp"
#include "ui_widget.hpp"
#include "ui_painter.hpp"
#include <cstdint>
namespace ui {
class RSSI : public Widget {
public:
constexpr RSSI(
const Rect parent_rect
) : Widget { parent_rect },
min_ { 0 },
avg_ { 0 },
max_ { 0 }
{
}
void on_show() override;
void on_hide() override;
void paint(Painter& painter) override;
private:
int32_t min_;
int32_t avg_;
int32_t max_;
void on_statistics_update(const RSSIStatistics& statistics);
};
}
#endif/*__UI_RSSI_H__*/

View File

@ -0,0 +1,125 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_setup.hpp"
#include "lpc43xx_cpp.hpp"
using namespace lpc43xx;
namespace ui {
SetDateTimeView::SetDateTimeView(
NavigationView& nav
) {
button_ok.on_select = [&nav, this](Button&){
const auto model = this->form_collect();
const rtc::RTC new_datetime {
model.year, model.month, model.day,
model.hour, model.minute, model.second
};
rtcSetTime(&RTCD1, &new_datetime);
nav.pop();
},
button_cancel.on_select = [&nav](Button&){
nav.pop();
},
add_children({ {
&text_title,
&field_year,
&text_slash1,
&field_month,
&text_slash2,
&field_day,
&field_hour,
&text_colon1,
&field_minute,
&text_colon2,
&field_second,
&text_format,
&button_ok,
&button_cancel,
} });
rtc::RTC datetime;
rtcGetTime(&RTCD1, &datetime);
SetDateTimeModel model {
datetime.year(),
datetime.month(),
datetime.day(),
datetime.hour(),
datetime.minute(),
datetime.second()
};
form_init(model);
}
void SetDateTimeView::focus() {
button_cancel.focus();
}
void SetDateTimeView::form_init(const SetDateTimeModel model) {
field_year.set_value(model.year);
field_month.set_value(model.month);
field_day.set_value(model.day);
field_hour.set_value(model.hour);
field_minute.set_value(model.minute);
field_second.set_value(model.second);
}
SetDateTimeModel SetDateTimeView::form_collect() {
return {
.year = static_cast<uint16_t>(field_year.value()),
.month = static_cast<uint8_t>(field_month.value()),
.day = static_cast<uint8_t>(field_day.value()),
.hour = static_cast<uint8_t>(field_hour.value()),
.minute = static_cast<uint8_t>(field_minute.value()),
.second = static_cast<uint8_t>(field_second.value())
};
}
AboutView::AboutView(NavigationView& nav) {
add_children({ {
&text_title,
&text_firmware,
&text_cpld_hackrf,
&text_cpld_portapack,
&button_ok,
} });
button_ok.on_select = [&nav](Button&){ nav.pop(); };
}
void AboutView::focus() {
button_ok.focus();
}
SetupMenuView::SetupMenuView(NavigationView& nav) {
add_items<2>({ {
{ "Date/Time", [&nav](){ nav.push(new SetDateTimeView { nav }); } },
{ "Touch", [&nav](){ nav.push(new NotImplementedView { nav }); } },
} });
on_left = [&nav](){ nav.pop(); };
}
} /* namespace ui */

View File

@ -0,0 +1,175 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __UI_SETUP_H__
#define __UI_SETUP_H__
#include "ui_widget.hpp"
#include "ui_menu.hpp"
#include "ui_navigation.hpp"
#include <cstdint>
namespace ui {
struct SetDateTimeModel {
uint16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
};
class SetDateTimeView : public View {
public:
std::function<void(SetDateTimeModel)> on_ok;
std::function<void()> on_cancel;
SetDateTimeView(NavigationView& nav);
void focus() override;
private:
Text text_title {
{ 10 * 8, 7 * 16, 9 * 16, 16 },
"Date/Time"
};
NumberField field_year {
{ 4 * 8, 9 * 16 },
4,
{ 2015, 2099 },
1,
'0',
};
Text text_slash1 {
{ 8 * 8, 9 * 16, 1 * 8, 16 },
"/",
};
NumberField field_month {
{ 9 * 8, 9 * 16 },
2,
{ 1, 12 },
1,
'0',
};
Text text_slash2 {
{ 11 * 8, 9 * 16, 1 * 8, 16 },
"/",
};
NumberField field_day {
{ 12 * 8, 9 * 16 },
2,
{ 1, 31 },
1,
'0',
};
NumberField field_hour {
{ 15 * 8, 9 * 16 },
2,
{ 0, 23 },
1,
'0',
};
Text text_colon1 {
{ 17 * 8, 9 * 16, 1 * 8, 16 },
":"
};
NumberField field_minute {
{ 18 * 8, 9 * 16 },
2,
{ 0, 59 },
1,
'0',
};
Text text_colon2 {
{ 20 * 8, 9 * 16, 1 * 8, 16 },
":",
};
NumberField field_second {
{ 21 * 8, 9 * 16 },
2,
{ 0, 59 },
1,
'0',
};
Text text_format {
{ 4 * 8, 11 * 16, 19 * 8, 16 },
"YYYY/MM/DD HH:MM:SS",
};
Button button_ok {
{ 4 * 8, 13 * 16, 8 * 8, 24 },
"OK",
};
Button button_cancel {
{ 18 * 8, 13 * 16, 8 * 8, 24 },
"Cancel",
};
void form_init(const SetDateTimeModel model);
SetDateTimeModel form_collect();
};
class AboutView : public View {
public:
AboutView(NavigationView& nav);
void focus() override;
private:
Text text_title {
{ 100, 96, 40, 16 },
"About",
};
Text text_firmware {
{ 0, 128, 240, 16 },
"Firmware Version git-??????",
};
Text text_cpld_hackrf {
{ 0, 144, 240, 16 },
"HackRF CPLD CRC 0x????????",
};
Text text_cpld_portapack {
{ 0, 160, 240, 16 },
"PortaPack CPLD CRC 0x????????",
};
Button button_ok {
{ 72, 192, 96, 24 },
"OK"
};
};
class SetupMenuView : public MenuView {
public:
SetupMenuView(NavigationView& nav);
};
} /* namespace ui */
#endif/*__UI_SETUP_H__*/

View File

@ -0,0 +1,26 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_spectrum.hpp"
namespace ui {
} /* namespace ui */

View File

@ -0,0 +1,244 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __UI_SPECTRUM_H__
#define __UI_SPECTRUM_H__
#include "ui.hpp"
#include "ui_widget.hpp"
#include "spectrum_color_lut.hpp"
#include "lcd_ili9341.hpp"
#include "message.hpp"
#include <cstdint>
#include <array>
namespace ui {
namespace spectrum {
class FrequencyScale : public Widget {
public:
void set_scale(const uint32_t new_hz_per_pixel) {
if( hz_per_pixel != new_hz_per_pixel ) {
hz_per_pixel = new_hz_per_pixel;
set_dirty();
}
}
void set_spectrum_bandwidth(const uint32_t new_bandwidth) {
if( spectrum_bandwidth != new_bandwidth ) {
spectrum_bandwidth = new_bandwidth;
set_dirty();
}
}
void set_channel_filter(
const uint32_t pass_bandwidth,
const uint32_t stop_bandwidth
) {
if( (channel_filter_pass_bandwidth != pass_bandwidth) ||
(channel_filter_stop_bandwidth != stop_bandwidth) ) {
channel_filter_pass_bandwidth = pass_bandwidth;
channel_filter_stop_bandwidth = stop_bandwidth;
set_dirty();
}
}
void paint(Painter& painter) override {
if( !hz_per_pixel ) {
// Can't draw without non-zero scale.
return;
}
const auto r = screen_rect();
const auto x_center = r.width() / 2;
/*
const auto text = to_string_dec_int(bandwidth, 6);
painter.draw_string(
r.pos,
style(),
text
);
*/
if( channel_filter_pass_bandwidth ) {
const auto pass_width = channel_filter_pass_bandwidth / hz_per_pixel;
const auto stop_width = channel_filter_stop_bandwidth / hz_per_pixel;
const auto pass_x_lo = x_center - pass_width / 2;
const auto pass_x_hi = x_center + pass_width / 2;
if( channel_filter_stop_bandwidth ) {
const auto stop_x_lo = x_center - stop_width / 2;
const auto stop_x_hi = x_center + stop_width / 2;
const Rect r_stop_lo {
static_cast<Coord>(r.left() + stop_x_lo), r.top(),
static_cast<Dim>(pass_x_lo - stop_x_lo), r.height()
};
painter.fill_rectangle(
r_stop_lo,
Color::yellow()
);
const Rect r_stop_hi {
static_cast<Coord>(r.left() + pass_x_hi), r.top(),
static_cast<Dim>(stop_x_hi - pass_x_hi), r.height()
};
painter.fill_rectangle(
r_stop_hi,
Color::yellow()
);
}
const Rect r_pass {
static_cast<Coord>(r.left() + pass_x_lo), r.top(),
static_cast<Dim>(pass_x_hi - pass_x_lo), r.height()
};
painter.fill_rectangle(
r_pass,
Color::green()
);
}
const Rect tick {
static_cast<Coord>(r.left() + x_center), r.top(),
1, r.height()
};
painter.fill_rectangle(tick, Color::white());
const Rect bottom_separator {
r.left(), static_cast<Coord>(r.bottom() - 1),
r.width(), 1
};
painter.fill_rectangle(bottom_separator, Color::white());
}
private:
uint32_t spectrum_bandwidth { 0 };
uint32_t channel_filter_pass_bandwidth { 0 };
uint32_t channel_filter_stop_bandwidth { 0 };
uint32_t hz_per_pixel { 0 };
};
class WaterfallView : public Widget {
public:
void on_show() override {
const auto screen_r = screen_rect();
context().display.scroll_set_area(screen_r.top(), screen_r.bottom());
}
void on_hide() override {
/* TODO: Clear region to eliminate brief flash of content at un-shifted
* position?
*/
context().display.scroll_disable();
}
void paint(Painter& painter) override {
// Do nothing.
(void)painter;
}
void on_channel_spectrum(
const ChannelSpectrum& spectrum
) {
/* TODO: static_assert that message.spectrum.db.size() >= pixel_row.size() */
const auto& db = *spectrum.db;
std::array<Color, 240> pixel_row;
for(size_t i=0; i<120; i++) {
const auto pixel_color = spectrum_rgb3_lut[db[256 - 120 + i]];
pixel_row[i] = pixel_color;
}
for(size_t i=120; i<240; i++) {
const auto pixel_color = spectrum_rgb3_lut[db[i - 120]];
pixel_row[i] = pixel_color;
}
auto& display = context().display;
const auto draw_y = display.scroll(1);
display.draw_pixels(
{ { 0, draw_y }, { pixel_row.size(), 1 } },
pixel_row
);
}
};
class WaterfallWidget : public View {
public:
WaterfallWidget() {
add_children({
&waterfall_view,
&frequency_scale,
});
}
void on_show() override {
context().message_map[Message::ID::ChannelSpectrum] = [this](const Message* const p) {
this->on_channel_spectrum(reinterpret_cast<const ChannelSpectrumMessage*>(p)->spectrum);
};
}
void on_hide() override {
context().message_map[Message::ID::ChannelSpectrum] = nullptr;
}
void set_parent_rect(const Rect new_parent_rect) override {
constexpr Dim scale_height = 16;
View::set_parent_rect(new_parent_rect);
frequency_scale.set_parent_rect({ 0, 0, new_parent_rect.width(), scale_height });
waterfall_view.set_parent_rect({
0, scale_height,
new_parent_rect.width(),
static_cast<Dim>(new_parent_rect.height() - scale_height)
});
}
void paint(Painter& painter) override {
// TODO:
(void)painter;
}
private:
WaterfallView waterfall_view;
FrequencyScale frequency_scale;
void on_channel_spectrum(const ChannelSpectrum& spectrum) {
waterfall_view.on_channel_spectrum(spectrum);
frequency_scale.set_scale(spectrum.bandwidth / spectrum.db_count);
// TODO: Set with actual information.
//taps_64_lp_042_078_tfilter
frequency_scale.set_channel_filter(spectrum.bandwidth * 42 / 1000, spectrum.bandwidth * 78 / 1000);
}
};
} /* namespace spectrum */
} /* namespace ui */
#endif/*__UI_SPECTRUM_H__*/

267
firmware/baseband/Makefile Executable file
View File

@ -0,0 +1,267 @@
#
# Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
#
# This file is part of PortaPack.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to
# the Free Software Foundation, Inc., 51 Franklin Street,
# Boston, MA 02110-1301, USA.
#
##############################################################################
# Build global options
# NOTE: Can be overridden externally.
#
# Compiler options here.
ifeq ($(USE_OPT),)
USE_OPT = -mthumb \
-O3 -ggdb3 \
-ffunction-sections \
-fdata-sections \
-fno-builtin \
-falign-functions=16 \
-fno-math-errno \
--specs=nano.specs
#-fomit-frame-pointer
endif
# C specific options here (added to USE_OPT).
ifeq ($(USE_COPT),)
USE_COPT = -std=gnu99
endif
# C++ specific options here (added to USE_OPT).
ifeq ($(USE_CPPOPT),)
USE_CPPOPT = -std=c++11 -fno-rtti -fno-exceptions
endif
# Enable this if you want the linker to remove unused code and data
ifeq ($(USE_LINK_GC),)
USE_LINK_GC = yes
endif
# Linker extra options here.
ifeq ($(USE_LDOPT),)
USE_LDOPT =
endif
# Enable this if you want link time optimizations (LTO)
ifeq ($(USE_LTO),)
USE_LTO = no
endif
# If enabled, this option allows to compile the application in THUMB mode.
ifeq ($(USE_THUMB),)
USE_THUMB = yes
endif
# Enable this if you want to see the full log while compiling.
ifeq ($(USE_VERBOSE_COMPILE),)
USE_VERBOSE_COMPILE = no
endif
#
# Build global options
##############################################################################
##############################################################################
# Architecture or project specific options
#
# Enables the use of FPU on Cortex-M4 (no, softfp, hard).
ifeq ($(USE_FPU),)
USE_FPU = hard
endif
#
# Architecture or project specific options
##############################################################################
##############################################################################
# Project, sources and paths
#
# Define project name here
PROJECT = baseband
# Imported source files and paths
CHIBIOS = ../chibios
CHIBIOS_PORTAPACK = ../chibios-portapack
include $(CHIBIOS_PORTAPACK)/boards/GSG_HACKRF_ONE/board.mk
include $(CHIBIOS_PORTAPACK)/os/hal/platforms/LPC43xx_M4/platform.mk
include $(CHIBIOS)/os/hal/hal.mk
include $(CHIBIOS_PORTAPACK)/os/ports/GCC/ARMCMx/LPC43xx_M4/port.mk
include $(CHIBIOS)/os/kernel/kernel.mk
include $(CHIBIOS)/test/test.mk
# Define linker script file here
LDSCRIPT= $(PORTLD)/LPC43xx_M4.ld
# C sources that can be compiled in ARM or THUMB mode depending on the global
# setting.
CSRC = $(PORTSRC) \
$(KERNSRC) \
$(TESTSRC) \
$(HALSRC) \
$(PLATFORMSRC) \
$(BOARDSRC)
# C++ sources that can be compiled in ARM or THUMB mode depending on the global
# setting.
CPPSRC = main.cpp \
message_queue.cpp \
event_m4.cpp \
gpdma.cpp \
baseband_dma.cpp \
portapack_shared_memory.cpp \
dsp_decimate.cpp \
dsp_demodulate.cpp \
clock_recovery.cpp \
access_code_correlator.cpp \
packet_builder.cpp \
dsp_fft.cpp \
dsp_fir_taps.cpp \
fxpt_atan2.cpp \
rssi.cpp \
rssi_dma.cpp \
audio.cpp \
audio_dma.cpp \
touch_dma.cpp \
../common/utility.cpp \
../common/debug.cpp \
../common/gcc.cpp
# C sources to be compiled in ARM mode regardless of the global setting.
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
# option that results in lower performance and larger code size.
ACSRC =
# C++ sources to be compiled in ARM mode regardless of the global setting.
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
# option that results in lower performance and larger code size.
ACPPSRC =
# C sources to be compiled in THUMB mode regardless of the global setting.
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
# option that results in lower performance and larger code size.
TCSRC =
# C sources to be compiled in THUMB mode regardless of the global setting.
# NOTE: Mixing ARM and THUMB mode enables the -mthumb-interwork compiler
# option that results in lower performance and larger code size.
TCPPSRC =
# List ASM source files here
ASMSRC = $(PORTASM)
INCDIR = ../common $(PORTINC) $(KERNINC) $(TESTINC) \
$(HALINC) $(PLATFORMINC) $(BOARDINC) \
$(CHIBIOS)/os/various
#
# Project, sources and paths
##############################################################################
##############################################################################
# Compiler settings
#
MCU = cortex-m4
#TRGT = arm-elf-
TRGT = arm-none-eabi-
CC = $(TRGT)gcc
CPPC = $(TRGT)g++
# Enable loading with g++ only if you need C++ runtime support.
# NOTE: You can use C++ even without C++ support if you are careful. C++
# runtime support makes code size explode.
#LD = $(TRGT)gcc
LD = $(TRGT)g++
CP = $(TRGT)objcopy
AS = $(TRGT)gcc -x assembler-with-cpp
OD = $(TRGT)objdump
SZ = $(TRGT)size
HEX = $(CP) -O ihex
BIN = $(CP) -O binary
# ARM-specific options here
AOPT =
# THUMB-specific options here
TOPT = -mthumb -DTHUMB
# Define C warning options here
CWARN = -Wall -Wextra -Wstrict-prototypes
# Define C++ warning options here
CPPWARN = -Wall -Wextra
#
# Compiler settings
##############################################################################
##############################################################################
# Start of default section
#
# List all default C defines here, like -D_DEBUG=1
# TODO: Switch -DCRT0_INIT_DATA depending on load from RAM or SPIFI?
# NOTE: _RANDOM_TCC to kill a GCC 4.9.3 error with std::max argument types
DDEFS = -DLPC43XX -DLPC43XX_M4 -D__NEWLIB__ -DHACKRF_ONE \
-DTOOLCHAIN_GCC -DTOOLCHAIN_GCC_ARM -D_RANDOM_TCC=0
# List all default ASM defines here, like -D_DEBUG=1
DADEFS =
# List all default directories to look for include files here
DINCDIR =
# List the default directory to look for the libraries here
DLIBDIR =
# List all default libraries here
DLIBS =
#
# End of default section
##############################################################################
##############################################################################
# Start of user section
#
# List all user C define here, like -D_DEBUG=1
UDEFS =
# Define ASM defines here
UADEFS =
# List all user directories here
UINCDIR =
# List the user directory to look for the libraries here
ULIBDIR =
# List all user libraries here
ULIBS =
#
# End of user defines
##############################################################################
RULESPATH = $(CHIBIOS)/os/ports/GCC/ARMCMx
include $(RULESPATH)/rules.mk

View File

@ -0,0 +1,44 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "access_code_correlator.hpp"
void AccessCodeCorrelator::configure(
const uint32_t new_code,
const size_t new_code_length,
const size_t new_maximum_hamming_distance
) {
if( new_code_length <= 32 ) {
code = new_code;
mask = mask_value(new_code_length);
maximum_hamming_distance = new_maximum_hamming_distance;
}
}
bool AccessCodeCorrelator::execute(
const uint_fast8_t in
) {
history = (history << 1) | (in & 1);
const auto delta_bits = (history ^ code) & mask;
//const size_t count = __builtin_popcountll(delta_bits);
const size_t count = __builtin_popcountl(delta_bits);
return (count <= maximum_hamming_distance);
}

View File

@ -0,0 +1,49 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __ACCESS_CODE_CORRELATOR_H__
#define __ACCESS_CODE_CORRELATOR_H__
#include <cstdint>
#include <cstddef>
class AccessCodeCorrelator {
public:
void configure(
const uint32_t new_code,
const size_t new_code_length,
const size_t new_maximum_hamming_distance
);
bool execute(const uint_fast8_t in);
private:
uint32_t code { 0 };
uint32_t mask { 0 };
uint32_t history { 0 };
size_t maximum_hamming_distance { 0 };
static constexpr uint32_t mask_value(const size_t n) {
return static_cast<uint32_t>((1ULL << n) - 1ULL);
}
};
#endif/*__ACCESS_CODE_CORRELATOR_H__*/

View File

@ -0,0 +1,238 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "audio_dma.hpp"
#include <cstdint>
#include <cstddef>
#include <array>
#include "hal.h"
#include "gpdma.hpp"
using namespace lpc43xx;
#include "portapack_dma.hpp"
namespace audio {
namespace dma {
constexpr uint32_t gpdma_ahb_master_peripheral = 1;
constexpr uint32_t gpdma_ahb_master_memory = 0;
constexpr uint32_t gpdma_ahb_master_lli_fetch = 0;
constexpr uint32_t gpdma_rx_peripheral = 0x9; /* I2S0 DMA request 1 */
constexpr uint32_t gpdma_rx_src_peripheral = gpdma_rx_peripheral;
constexpr uint32_t gpdma_rx_dest_peripheral = gpdma_rx_peripheral;
constexpr uint32_t gpdma_tx_peripheral = 0xa; /* I2S0 DMA request 2 */
constexpr uint32_t gpdma_tx_src_peripheral = gpdma_tx_peripheral;
constexpr uint32_t gpdma_tx_dest_peripheral = gpdma_tx_peripheral;
constexpr gpdma::channel::LLIPointer lli_pointer(const void* lli) {
return {
.lm = gpdma_ahb_master_lli_fetch,
.r = 0,
.lli = reinterpret_cast<uint32_t>(lli),
};
}
constexpr gpdma::channel::Control control_tx(const size_t transfer_bytes) {
return {
.transfersize = gpdma::buffer_words(transfer_bytes, 4),
.sbsize = 4, /* Burst size: 32 */
.dbsize = 4, /* Burst size: 32 */
.swidth = 2, /* Source transfer width: word (32 bits) */
.dwidth = 2, /* Destination transfer width: word (32 bits) */
.s = gpdma_ahb_master_memory,
.d = gpdma_ahb_master_peripheral,
.si = 1,
.di = 0,
.prot1 = 0,
.prot2 = 0,
.prot3 = 0,
.i = 1,
};
}
constexpr gpdma::channel::Config config_tx() {
return {
.e = 0,
.srcperipheral = gpdma_tx_src_peripheral,
.destperipheral = gpdma_tx_dest_peripheral,
.flowcntrl = gpdma::FlowControl::MemoryToPeripheral_DMAControl,
.ie = 1,
.itc = 1,
.l = 0,
.a = 0,
.h = 0,
};
}
constexpr gpdma::channel::Control control_rx(const size_t transfer_bytes) {
return {
.transfersize = gpdma::buffer_words(transfer_bytes, 4),
.sbsize = 4, /* Burst size: 32 */
.dbsize = 4, /* Burst size: 32 */
.swidth = 2, /* Source transfer width: word (32 bits) */
.dwidth = 2, /* Destination transfer width: word (32 bits) */
.s = gpdma_ahb_master_peripheral,
.d = gpdma_ahb_master_memory,
.si = 0,
.di = 1,
.prot1 = 0,
.prot2 = 0,
.prot3 = 0,
.i = 1,
};
}
constexpr gpdma::channel::Config config_rx() {
return {
.e = 0,
.srcperipheral = gpdma_rx_src_peripheral,
.destperipheral = gpdma_rx_dest_peripheral,
.flowcntrl = gpdma::FlowControl::PeripheralToMemory_DMAControl,
.ie = 1,
.itc = 1,
.l = 0,
.a = 0,
.h = 0,
};
}
/* TODO: Clean up terminology around "buffer", "transfer", "samples" */
constexpr size_t buffer_samples_log2n = 7;
constexpr size_t buffer_samples = (1 << buffer_samples_log2n);
constexpr size_t transfers_per_buffer_log2n = 2;
constexpr size_t transfers_per_buffer = (1 << transfers_per_buffer_log2n);
constexpr size_t transfer_samples = buffer_samples / transfers_per_buffer;
constexpr size_t transfers_mask = transfers_per_buffer - 1;
constexpr size_t buffer_bytes = buffer_samples * sizeof(sample_t);
constexpr size_t transfer_bytes = transfer_samples * sizeof(sample_t);
static std::array<sample_t, buffer_samples> buffer_tx;
static std::array<sample_t, buffer_samples> buffer_rx;
static std::array<gpdma::channel::LLI, transfers_per_buffer> lli_tx_loop;
static std::array<gpdma::channel::LLI, transfers_per_buffer> lli_rx_loop;
static constexpr auto& gpdma_channel_i2s0_tx = gpdma::channels[portapack::i2s0_tx_gpdma_channel_number];
static constexpr auto& gpdma_channel_i2s0_rx = gpdma::channels[portapack::i2s0_rx_gpdma_channel_number];
static volatile const gpdma::channel::LLI* tx_next_lli = nullptr;
static volatile const gpdma::channel::LLI* rx_next_lli = nullptr;
static void tx_transfer_complete() {
tx_next_lli = gpdma_channel_i2s0_tx.next_lli();
}
static void tx_error() {
disable();
}
static void rx_transfer_complete() {
rx_next_lli = gpdma_channel_i2s0_rx.next_lli();
}
static void rx_error() {
disable();
}
void init() {
gpdma_channel_i2s0_tx.set_handlers(tx_transfer_complete, tx_error);
gpdma_channel_i2s0_rx.set_handlers(rx_transfer_complete, rx_error);
// LPC_GPDMA->SYNC |= (1 << gpdma_rx_peripheral);
// LPC_GPDMA->SYNC |= (1 << gpdma_tx_peripheral);
}
static void configure_tx() {
const auto peripheral = reinterpret_cast<uint32_t>(&LPC_I2S0->TXFIFO);
const auto control_value = control_tx(transfer_bytes);
for(size_t i=0; i<lli_tx_loop.size(); i++) {
const auto memory = reinterpret_cast<uint32_t>(&buffer_tx[i * transfer_samples]);
lli_tx_loop[i].srcaddr = memory;
lli_tx_loop[i].destaddr = peripheral;
lli_tx_loop[i].lli = lli_pointer(&lli_tx_loop[(i + 1) % lli_tx_loop.size()]);
lli_tx_loop[i].control = control_value;
}
}
static void configure_rx() {
const auto peripheral = reinterpret_cast<uint32_t>(&LPC_I2S0->RXFIFO);
const auto control_value = control_rx(transfer_bytes);
for(size_t i=0; i<lli_rx_loop.size(); i++) {
const auto memory = reinterpret_cast<uint32_t>(&buffer_rx[i * transfer_samples]);
lli_rx_loop[i].srcaddr = peripheral;
lli_rx_loop[i].destaddr = memory;
lli_rx_loop[i].lli = lli_pointer(&lli_rx_loop[(i + 1) % lli_rx_loop.size()]);
lli_rx_loop[i].control = control_value;
}
}
void configure() {
configure_tx();
configure_rx();
}
void enable() {
const auto gpdma_config_tx = config_tx();
const auto gpdma_config_rx = config_rx();
gpdma_channel_i2s0_tx.configure(lli_tx_loop[0], gpdma_config_tx);
gpdma_channel_i2s0_rx.configure(lli_rx_loop[0], gpdma_config_rx);
gpdma_channel_i2s0_tx.enable();
gpdma_channel_i2s0_rx.enable();
}
void disable() {
gpdma_channel_i2s0_tx.disable_force();
gpdma_channel_i2s0_rx.disable_force();
}
buffer_t tx_empty_buffer() {
const auto next_lli = tx_next_lli;
if( next_lli ) {
const size_t next_index = next_lli - &lli_tx_loop[0];
const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask;
return { reinterpret_cast<sample_t*>(lli_tx_loop[free_index].srcaddr), transfer_samples };
} else {
return { nullptr, 0 };
}
}
buffer_t rx_empty_buffer() {
const auto next_lli = rx_next_lli;
if( next_lli ) {
const size_t next_index = next_lli - &lli_rx_loop[0];
const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask;
return { reinterpret_cast<sample_t*>(lli_rx_loop[free_index].srcaddr), transfer_samples };
} else {
return { nullptr, 0 };
}
}
} /* namespace dma */
} /* namespace audio */

View File

@ -0,0 +1,43 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __AUDIO_DMA_H__
#define __AUDIO_DMA_H__
#include <cstdint>
#include "audio.hpp"
namespace audio {
namespace dma {
void init();
void configure();
void enable();
void disable();
audio::buffer_t tx_empty_buffer();
audio::buffer_t rx_empty_buffer();
} /* namespace dma */
} /* namespace audio */
#endif/*__AUDIO_DMA_H__*/

View File

@ -0,0 +1,180 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "baseband_dma.hpp"
#include <cstdint>
#include <cstddef>
#include <array>
#include "hal.h"
#include "gpdma.hpp"
using namespace lpc43xx;
#include "portapack_dma.hpp"
namespace baseband {
namespace dma {
constexpr uint32_t gpdma_ahb_master_sgpio = 0;
constexpr uint32_t gpdma_ahb_master_memory = 1;
constexpr uint32_t gpdma_ahb_master_lli_fetch = 0;
constexpr uint32_t gpdma_src_peripheral = 0x0;
constexpr uint32_t gpdma_dest_peripheral = 0x0;
constexpr gpdma::channel::LLIPointer lli_pointer(const void* lli) {
return {
.lm = gpdma_ahb_master_lli_fetch,
.r = 0,
.lli = reinterpret_cast<uint32_t>(lli),
};
}
constexpr gpdma::channel::Control control(const baseband::Direction direction, const size_t buffer_words) {
return {
.transfersize = buffer_words,
.sbsize = 0, /* Burst size: 1 */
.dbsize = 0, /* Burst size: 1 */
.swidth = 2, /* Source transfer width: word (32 bits) */
.dwidth = 2, /* Destination transfer width: word (32 bits) */
.s = (direction == baseband::Direction::Transmit) ? gpdma_ahb_master_memory : gpdma_ahb_master_sgpio,
.d = (direction == baseband::Direction::Transmit) ? gpdma_ahb_master_sgpio : gpdma_ahb_master_memory,
.si = (direction == baseband::Direction::Transmit) ? 1U : 0U,
.di = (direction == baseband::Direction::Transmit) ? 0U : 1U,
.prot1 = 0,
.prot2 = 0,
.prot3 = 0,
.i = 1,
};
}
constexpr gpdma::channel::Config config(const baseband::Direction direction) {
return {
.e = 0,
.srcperipheral = gpdma_src_peripheral,
.destperipheral = gpdma_dest_peripheral,
.flowcntrl = (direction == baseband::Direction::Transmit)
? gpdma::FlowControl::MemoryToPeripheral_DMAControl
: gpdma::FlowControl::PeripheralToMemory_DMAControl,
.ie = 1,
.itc = 1,
.l = 0,
.a = 0,
.h = 0,
};
}
constexpr size_t buffer_samples_log2n = 13;
constexpr size_t buffer_samples = (1 << buffer_samples_log2n);
constexpr size_t transfers_per_buffer_log2n = 2;
constexpr size_t transfers_per_buffer = (1 << transfers_per_buffer_log2n);
constexpr size_t transfer_samples = buffer_samples / transfers_per_buffer;
constexpr size_t transfers_mask = transfers_per_buffer - 1;
constexpr size_t buffer_bytes = buffer_samples * sizeof(baseband::sample_t);
constexpr size_t transfer_bytes = transfer_samples * sizeof(baseband::sample_t);
constexpr size_t msg_count = transfers_per_buffer - 1;
static std::array<gpdma::channel::LLI, transfers_per_buffer> lli_loop;
static constexpr auto& gpdma_channel_sgpio = gpdma::channels[portapack::sgpio_gpdma_channel_number];
//static Mailbox mailbox;
//static std::array<msg_t, msg_count> messages;
static Semaphore semaphore;
static volatile const gpdma::channel::LLI* next_lli = nullptr;
static void transfer_complete() {
next_lli = gpdma_channel_sgpio.next_lli();
/* TODO: Is Mailbox the proper synchronization mechanism for this? */
//chMBPostI(&mailbox, 0);
chSemSignalI(&semaphore);
}
static void dma_error() {
disable();
}
void init() {
//chMBInit(&mailbox, messages.data(), messages.size());
chSemInit(&semaphore, 0);
gpdma_channel_sgpio.set_handlers(transfer_complete, dma_error);
// LPC_GPDMA->SYNC |= (1 << gpdma_src_peripheral);
// LPC_GPDMA->SYNC |= (1 << gpdma_dest_peripheral);
}
void configure(
baseband::sample_t* const buffer_base,
const baseband::Direction direction
) {
const auto peripheral = reinterpret_cast<uint32_t>(&LPC_SGPIO->REG_SS[0]);
const auto control_value = control(direction, gpdma::buffer_words(transfer_bytes, 4));
for(size_t i=0; i<lli_loop.size(); i++) {
const auto memory = reinterpret_cast<uint32_t>(&buffer_base[i * transfer_samples]);
lli_loop[i].srcaddr = (direction == Direction::Transmit) ? memory : peripheral;
lli_loop[i].destaddr = (direction == Direction::Transmit) ? peripheral : memory;
lli_loop[i].lli = lli_pointer(&lli_loop[(i + 1) % lli_loop.size()]);
lli_loop[i].control = control_value;
}
}
void enable(const baseband::Direction direction) {
const auto gpdma_config = config(direction);
gpdma_channel_sgpio.configure(lli_loop[0], gpdma_config);
//chMBReset(&mailbox);
chSemReset(&semaphore, 0);
gpdma_channel_sgpio.enable();
}
bool is_enabled() {
return gpdma_channel_sgpio.is_enabled();
}
void disable() {
gpdma_channel_sgpio.disable_force();
}
baseband::buffer_t wait_for_rx_buffer() {
//msg_t msg;
//const auto status = chMBFetch(&mailbox, &msg, TIME_INFINITE);
const auto status = chSemWait(&semaphore);
if( status == RDY_OK ) {
const auto next = next_lli;
if( next ) {
const size_t next_index = next - &lli_loop[0];
const size_t free_index = (next_index + transfers_per_buffer - 2) & transfers_mask;
return { reinterpret_cast<sample_t*>(lli_loop[free_index].destaddr), transfer_samples };
} else {
return { nullptr, 0 };
}
} else {
return { nullptr, 0 };
}
}
} /* namespace dma */
} /* namespace baseband */

View File

@ -0,0 +1,52 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __BASEBAND_DMA_H__
#define __BASEBAND_DMA_H__
#include <cstddef>
#include <array>
#include "complex.hpp"
#include "baseband.hpp"
namespace baseband {
namespace dma {
using Handler = void (*)();
void init();
void configure(
baseband::sample_t* const buffer_base,
const baseband::Direction direction
);
void enable(const baseband::Direction direction);
bool is_enabled();
void disable();
baseband::buffer_t wait_for_rx_buffer();
} /* namespace dma */
} /* namespace baseband */
#endif/*__BASEBAND_DMA_H__*/

View File

@ -0,0 +1,90 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __BLOCK_DECIMATOR_H__
#define __BLOCK_DECIMATOR_H__
#include <cstdint>
#include <cstddef>
#include "complex.hpp"
template<size_t N>
class BlockDecimator {
public:
constexpr BlockDecimator(
const size_t factor
) : factor { factor }
{
}
void set_input_sampling_rate(const uint32_t new_sampling_rate) {
if( new_sampling_rate != input_sampling_rate ) {
input_sampling_rate = new_sampling_rate;
reset_state();
}
}
void set_factor(const size_t new_factor) {
if( new_factor != factor ) {
factor = new_factor;
reset_state();
}
}
uint32_t output_sampling_rate() const {
return input_sampling_rate / factor;
}
template<typename BlockCallback>
void feed(const buffer_c16_t src, BlockCallback callback) {
/* NOTE: Input block size must be >= factor */
set_input_sampling_rate(src.sampling_rate);
while( src_i < src.count ) {
buffer[dst_i++] = src.p[src_i];
if( dst_i == buffer.size() ) {
callback({ buffer.data(), buffer.size(), output_sampling_rate() });
reset_state();
dst_i = 0;
}
src_i += factor;
}
src_i -= src.count;
}
private:
std::array<complex16_t, N> buffer;
uint32_t input_sampling_rate { 0 };
size_t factor { 1 };
size_t src_i { 0 };
size_t dst_i { 0 };
void reset_state() {
src_i = 0;
dst_i = 0;
}
};
#endif/*__BLOCK_DECIMATOR_H__*/

546
firmware/baseband/chconf.h Executable file
View File

@ -0,0 +1,546 @@
/*
ChibiOS/RT - Copyright (C) 2006-2013 Giovanni Di Sirio
Copyright (C) 2014 Jared Boone, ShareBrained Technology
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @file templates/chconf.h
* @brief Configuration file template.
* @details A copy of this file must be placed in each project directory, it
* contains the application specific kernel settings.
*
* @addtogroup config
* @details Kernel related settings and hooks.
* @{
*/
#ifndef _CHCONF_H_
#define _CHCONF_H_
/*===========================================================================*/
/**
* @name Kernel parameters and options
* @{
*/
/*===========================================================================*/
/**
* @brief System tick frequency.
* @details Frequency of the system timer that drives the system ticks. This
* setting also defines the system tick time unit.
*/
#if !defined(CH_FREQUENCY) || defined(__DOXYGEN__)
#define CH_FREQUENCY 1000
#endif
/**
* @brief Round robin interval.
* @details This constant is the number of system ticks allowed for the
* threads before preemption occurs. Setting this value to zero
* disables the preemption for threads with equal priority and the
* round robin becomes cooperative. Note that higher priority
* threads can still preempt, the kernel is always preemptive.
*
* @note Disabling the round robin preemption makes the kernel more compact
* and generally faster.
*/
#if !defined(CH_TIME_QUANTUM) || defined(__DOXYGEN__)
#define CH_TIME_QUANTUM 0
#endif
/**
* @brief Managed RAM size.
* @details Size of the RAM area to be managed by the OS. If set to zero
* then the whole available RAM is used. The core memory is made
* available to the heap allocator and/or can be used directly through
* the simplified core memory allocator.
*
* @note In order to let the OS manage the whole RAM the linker script must
* provide the @p __heap_base__ and @p __heap_end__ symbols.
* @note Requires @p CH_USE_MEMCORE.
*/
#if !defined(CH_MEMCORE_SIZE) || defined(__DOXYGEN__)
#define CH_MEMCORE_SIZE 0
#endif
/**
* @brief Idle thread automatic spawn suppression.
* @details When this option is activated the function @p chSysInit()
* does not spawn the idle thread automatically. The application has
* then the responsibility to do one of the following:
* - Spawn a custom idle thread at priority @p IDLEPRIO.
* - Change the main() thread priority to @p IDLEPRIO then enter
* an endless loop. In this scenario the @p main() thread acts as
* the idle thread.
* .
* @note Unless an idle thread is spawned the @p main() thread must not
* enter a sleep state.
*/
#if !defined(CH_NO_IDLE_THREAD) || defined(__DOXYGEN__)
#define CH_NO_IDLE_THREAD FALSE
#endif
/** @} */
/*===========================================================================*/
/**
* @name Performance options
* @{
*/
/*===========================================================================*/
/**
* @brief OS optimization.
* @details If enabled then time efficient rather than space efficient code
* is used when two possible implementations exist.
*
* @note This is not related to the compiler optimization options.
* @note The default is @p TRUE.
*/
#if !defined(CH_OPTIMIZE_SPEED) || defined(__DOXYGEN__)
#define CH_OPTIMIZE_SPEED TRUE
#endif
/** @} */
/*===========================================================================*/
/**
* @name Subsystem options
* @{
*/
/*===========================================================================*/
/**
* @brief Threads registry APIs.
* @details If enabled then the registry APIs are included in the kernel.
*
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_REGISTRY) || defined(__DOXYGEN__)
#define CH_USE_REGISTRY TRUE
#endif
/**
* @brief Threads synchronization APIs.
* @details If enabled then the @p chThdWait() function is included in
* the kernel.
*
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_WAITEXIT) || defined(__DOXYGEN__)
#define CH_USE_WAITEXIT TRUE
#endif
/**
* @brief Semaphores APIs.
* @details If enabled then the Semaphores APIs are included in the kernel.
*
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_SEMAPHORES) || defined(__DOXYGEN__)
#define CH_USE_SEMAPHORES TRUE
#endif
/**
* @brief Semaphores queuing mode.
* @details If enabled then the threads are enqueued on semaphores by
* priority rather than in FIFO order.
*
* @note The default is @p FALSE. Enable this if you have special requirements.
* @note Requires @p CH_USE_SEMAPHORES.
*/
#if !defined(CH_USE_SEMAPHORES_PRIORITY) || defined(__DOXYGEN__)
#define CH_USE_SEMAPHORES_PRIORITY FALSE
#endif
/**
* @brief Atomic semaphore API.
* @details If enabled then the semaphores the @p chSemSignalWait() API
* is included in the kernel.
*
* @note The default is @p TRUE.
* @note Requires @p CH_USE_SEMAPHORES.
*/
#if !defined(CH_USE_SEMSW) || defined(__DOXYGEN__)
#define CH_USE_SEMSW TRUE
#endif
/**
* @brief Mutexes APIs.
* @details If enabled then the mutexes APIs are included in the kernel.
*
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_MUTEXES) || defined(__DOXYGEN__)
#define CH_USE_MUTEXES TRUE
#endif
/**
* @brief Conditional Variables APIs.
* @details If enabled then the conditional variables APIs are included
* in the kernel.
*
* @note The default is @p TRUE.
* @note Requires @p CH_USE_MUTEXES.
*/
#if !defined(CH_USE_CONDVARS) || defined(__DOXYGEN__)
#define CH_USE_CONDVARS TRUE
#endif
/**
* @brief Conditional Variables APIs with timeout.
* @details If enabled then the conditional variables APIs with timeout
* specification are included in the kernel.
*
* @note The default is @p TRUE.
* @note Requires @p CH_USE_CONDVARS.
*/
#if !defined(CH_USE_CONDVARS_TIMEOUT) || defined(__DOXYGEN__)
#define CH_USE_CONDVARS_TIMEOUT TRUE
#endif
/**
* @brief Events Flags APIs.
* @details If enabled then the event flags APIs are included in the kernel.
*
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_EVENTS) || defined(__DOXYGEN__)
#define CH_USE_EVENTS TRUE
#endif
/**
* @brief Events Flags APIs with timeout.
* @details If enabled then the events APIs with timeout specification
* are included in the kernel.
*
* @note The default is @p TRUE.
* @note Requires @p CH_USE_EVENTS.
*/
#if !defined(CH_USE_EVENTS_TIMEOUT) || defined(__DOXYGEN__)
#define CH_USE_EVENTS_TIMEOUT TRUE
#endif
/**
* @brief Synchronous Messages APIs.
* @details If enabled then the synchronous messages APIs are included
* in the kernel.
*
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_MESSAGES) || defined(__DOXYGEN__)
#define CH_USE_MESSAGES TRUE
#endif
/**
* @brief Synchronous Messages queuing mode.
* @details If enabled then messages are served by priority rather than in
* FIFO order.
*
* @note The default is @p FALSE. Enable this if you have special requirements.
* @note Requires @p CH_USE_MESSAGES.
*/
#if !defined(CH_USE_MESSAGES_PRIORITY) || defined(__DOXYGEN__)
#define CH_USE_MESSAGES_PRIORITY FALSE
#endif
/**
* @brief Mailboxes APIs.
* @details If enabled then the asynchronous messages (mailboxes) APIs are
* included in the kernel.
*
* @note The default is @p TRUE.
* @note Requires @p CH_USE_SEMAPHORES.
*/
#if !defined(CH_USE_MAILBOXES) || defined(__DOXYGEN__)
#define CH_USE_MAILBOXES TRUE
#endif
/**
* @brief I/O Queues APIs.
* @details If enabled then the I/O queues APIs are included in the kernel.
*
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_QUEUES) || defined(__DOXYGEN__)
#define CH_USE_QUEUES TRUE
#endif
/**
* @brief Core Memory Manager APIs.
* @details If enabled then the core memory manager APIs are included
* in the kernel.
*
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_MEMCORE) || defined(__DOXYGEN__)
#define CH_USE_MEMCORE TRUE
#endif
/**
* @brief Heap Allocator APIs.
* @details If enabled then the memory heap allocator APIs are included
* in the kernel.
*
* @note The default is @p TRUE.
* @note Requires @p CH_USE_MEMCORE and either @p CH_USE_MUTEXES or
* @p CH_USE_SEMAPHORES.
* @note Mutexes are recommended.
*/
#if !defined(CH_USE_HEAP) || defined(__DOXYGEN__)
#define CH_USE_HEAP TRUE
#endif
/**
* @brief C-runtime allocator.
* @details If enabled the the heap allocator APIs just wrap the C-runtime
* @p malloc() and @p free() functions.
*
* @note The default is @p FALSE.
* @note Requires @p CH_USE_HEAP.
* @note The C-runtime may or may not require @p CH_USE_MEMCORE, see the
* appropriate documentation.
*/
#if !defined(CH_USE_MALLOC_HEAP) || defined(__DOXYGEN__)
#define CH_USE_MALLOC_HEAP FALSE
#endif
/**
* @brief Memory Pools Allocator APIs.
* @details If enabled then the memory pools allocator APIs are included
* in the kernel.
*
* @note The default is @p TRUE.
*/
#if !defined(CH_USE_MEMPOOLS) || defined(__DOXYGEN__)
#define CH_USE_MEMPOOLS TRUE
#endif
/**
* @brief Dynamic Threads APIs.
* @details If enabled then the dynamic threads creation APIs are included
* in the kernel.
*
* @note The default is @p TRUE.
* @note Requires @p CH_USE_WAITEXIT.
* @note Requires @p CH_USE_HEAP and/or @p CH_USE_MEMPOOLS.
*/
#if !defined(CH_USE_DYNAMIC) || defined(__DOXYGEN__)
#define CH_USE_DYNAMIC TRUE
#endif
/** @} */
/*===========================================================================*/
/**
* @name Debug options
* @{
*/
/*===========================================================================*/
/**
* @brief Debug option, system state check.
* @details If enabled the correct call protocol for system APIs is checked
* at runtime.
*
* @note The default is @p FALSE.
*/
#if !defined(CH_DBG_SYSTEM_STATE_CHECK) || defined(__DOXYGEN__)
#define CH_DBG_SYSTEM_STATE_CHECK TRUE
#endif
/**
* @brief Debug option, parameters checks.
* @details If enabled then the checks on the API functions input
* parameters are activated.
*
* @note The default is @p FALSE.
*/
#if !defined(CH_DBG_ENABLE_CHECKS) || defined(__DOXYGEN__)
#define CH_DBG_ENABLE_CHECKS TRUE
#endif
/**
* @brief Debug option, consistency checks.
* @details If enabled then all the assertions in the kernel code are
* activated. This includes consistency checks inside the kernel,
* runtime anomalies and port-defined checks.
*
* @note The default is @p FALSE.
*/
#if !defined(CH_DBG_ENABLE_ASSERTS) || defined(__DOXYGEN__)
#define CH_DBG_ENABLE_ASSERTS TRUE
#endif
/**
* @brief Debug option, trace buffer.
* @details If enabled then the context switch circular trace buffer is
* activated.
*
* @note The default is @p FALSE.
*/
#if !defined(CH_DBG_ENABLE_TRACE) || defined(__DOXYGEN__)
#define CH_DBG_ENABLE_TRACE FALSE
#endif
/**
* @brief Debug option, stack checks.
* @details If enabled then a runtime stack check is performed.
*
* @note The default is @p FALSE.
* @note The stack check is performed in a architecture/port dependent way.
* It may not be implemented or some ports.
* @note The default failure mode is to halt the system with the global
* @p panic_msg variable set to @p NULL.
*/
#if !defined(CH_DBG_ENABLE_STACK_CHECK) || defined(__DOXYGEN__)
#define CH_DBG_ENABLE_STACK_CHECK TRUE
#endif
/**
* @brief Debug option, stacks initialization.
* @details If enabled then the threads working area is filled with a byte
* value when a thread is created. This can be useful for the
* runtime measurement of the used stack.
*
* @note The default is @p FALSE.
*/
#if !defined(CH_DBG_FILL_THREADS) || defined(__DOXYGEN__)
#define CH_DBG_FILL_THREADS TRUE
#endif
/**
* @brief Debug option, threads profiling.
* @details If enabled then a field is added to the @p Thread structure that
* counts the system ticks occurred while executing the thread.
*
* @note The default is @p TRUE.
* @note This debug option is defaulted to TRUE because it is required by
* some test cases into the test suite.
*/
#if !defined(CH_DBG_THREADS_PROFILING) || defined(__DOXYGEN__)
#define CH_DBG_THREADS_PROFILING TRUE
#endif
/** @} */
/*===========================================================================*/
/**
* @name Kernel hooks
* @{
*/
/*===========================================================================*/
/**
* @brief Threads descriptor structure extension.
* @details User fields added to the end of the @p Thread structure.
*/
#if !defined(THREAD_EXT_FIELDS) || defined(__DOXYGEN__)
#define THREAD_EXT_FIELDS \
/* Add threads custom fields here.*/ \
uint32_t switches; \
uint32_t start_ticks; \
uint32_t total_ticks;
#endif
/**
* @brief Threads initialization hook.
* @details User initialization code added to the @p chThdInit() API.
*
* @note It is invoked from within @p chThdInit() and implicitly from all
* the threads creation APIs.
*/
#if !defined(THREAD_EXT_INIT_HOOK) || defined(__DOXYGEN__)
#define THREAD_EXT_INIT_HOOK(tp) { \
/* Add threads initialization code here.*/ \
tp->switches = 0; \
tp->start_ticks = 0; \
tp->total_ticks = 0; \
}
#endif
/**
* @brief Threads finalization hook.
* @details User finalization code added to the @p chThdExit() API.
*
* @note It is inserted into lock zone.
* @note It is also invoked when the threads simply return in order to
* terminate.
*/
#if !defined(THREAD_EXT_EXIT_HOOK) || defined(__DOXYGEN__)
#define THREAD_EXT_EXIT_HOOK(tp) { \
/* Add threads finalization code here.*/ \
}
#endif
/**
* @brief Context switch hook.
* @details This hook is invoked just before switching between threads.
*/
#if !defined(THREAD_CONTEXT_SWITCH_HOOK) || defined(__DOXYGEN__)
#define THREAD_CONTEXT_SWITCH_HOOK(ntp, otp) { \
/* System halt code here.*/ \
otp->switches++; \
ntp->start_ticks = *((volatile uint32_t*)0x400C4008); \
otp->total_ticks += (ntp->start_ticks - otp->start_ticks); \
}
#endif
/**
* @brief Idle Loop hook.
* @details This hook is continuously invoked by the idle thread loop.
*/
#if !defined(IDLE_LOOP_HOOK) || defined(__DOXYGEN__)
#define IDLE_LOOP_HOOK() { \
/* Idle loop code here.*/ \
}
#endif
/**
* @brief System tick event hook.
* @details This hook is invoked in the system tick handler immediately
* after processing the virtual timers queue.
*/
#if !defined(SYSTEM_TICK_EVENT_HOOK) || defined(__DOXYGEN__)
#define SYSTEM_TICK_EVENT_HOOK() { \
/* System tick event code here.*/ \
}
#endif
/**
* @brief System halt hook.
* @details This hook is invoked in case to a system halting error before
* the system is halted.
*/
#if !defined(SYSTEM_HALT_HOOK) || defined(__DOXYGEN__)
#define SYSTEM_HALT_HOOK() { \
/* System halt code here.*/ \
}
#endif
/** @} */
/*===========================================================================*/
/* Port-specific settings (override port settings defaulted in chcore.h). */
/*===========================================================================*/
/* NOTE: When changing this option you also have to enable or disable the FPU
in the project options.*/
#define CORTEX_USE_FPU TRUE
#define CORTEX_ENABLE_WFI_IDLE TRUE
#endif /* _CHCONF_H_ */
/** @} */

View File

@ -0,0 +1,31 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "clock_recovery.hpp"
void ClockRecovery::configure(
const uint32_t symbol_rate,
const uint32_t sampling_rate
) {
phase_increment = phase_increment_u32(
fractional_symbol_rate(symbol_rate, sampling_rate)
);
}

View File

@ -0,0 +1,86 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __CLOCK_RECOVERY_H__
#define __CLOCK_RECOVERY_H__
#include <cstdint>
class ClockRecovery {
public:
void configure(
const uint32_t symbol_rate,
const uint32_t sampling_rate
);
template<typename SymbolHandler>
void execute(
const float in,
SymbolHandler symbol_handler
) {
const bool phase_0 = (phase_last >> 31) & (!(phase >> 31));
const bool phase_180 = (!(phase_last >> 31)) & (phase >> 31);
phase_last = phase;
phase += phase_increment + phase_adjustment;
if( phase_0 || phase_180 ) {
t2 = t1;
t1 = t0;
t0 = in;
}
if( phase_0 ) {
symbol_handler(t0);
const float error = (t0 - t2) * t1;
// + error == late == decrease/slow phase
// - error == early == increase/fast phase
error_filtered = 0.75f * error_filtered + 0.25f * error;
// Correct phase (don't change frequency!)
phase_adjustment = -phase_increment * error_filtered / 200.0f;
}
}
private:
uint32_t phase { 0 };
uint32_t phase_last { 0 };
uint32_t phase_adjustment { 0 };
uint32_t phase_increment { 0 };
float t0 { 0 };
float t1 { 0 };
float t2 { 0 };
float error_filtered { 0 };
static constexpr float fractional_symbol_rate(
const uint32_t symbol_rate,
const uint32_t sampling_rate
) {
return float(symbol_rate) / float(sampling_rate);
}
static constexpr uint32_t phase_increment_u32(const float fractional_symbol_rate) {
return 4294967296.0f * fractional_symbol_rate;
}
};
#endif/*__CLOCK_RECOVERY_H__*/

View File

@ -0,0 +1,460 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "dsp_decimate.hpp"
#include <hal.h>
namespace dsp {
namespace decimate {
buffer_c16_t TranslateByFSOver4AndDecimateBy2CIC3::execute(buffer_c8_t src, buffer_c16_t dst) {
/* Translates incoming complex<int8_t> samples by -fs/4,
* decimates by two using a non-recursive third-order CIC filter.
*/
/* Derivation of algorithm:
* Original CIC filter (decimating by two):
* D_I0 = i3 * 1 + i2 * 3 + i1 * 3 + i0 * 1
* D_Q0 = q3 * 1 + q2 * 3 + q1 * 3 + q0 * 1
*
* D_I1 = i5 * 1 + i4 * 3 + i3 * 3 + i2 * 1
* D_Q1 = q5 * 1 + q4 * 3 + q3 * 3 + q2 * 1
*
* Translate -fs/4, phased 180 degrees, accomplished by complex multiplication
* of complex length-4 sequence:
*
* Substitute:
* i0 = -i0, q0 = -q0
* i1 = -q1, q1 = i1
* i2 = i2, q2 = q2
* i3 = q3, q3 = -i3
* i4 = -i4, q4 = -q4
* i5 = -q5, q5 = i5
*
* Resulting taps (with decimation by 2, four samples in, two samples out):
* D_I0 = q3 * 1 + i2 * 3 + -q1 * 3 + -i0 * 1
* D_Q0 = -i3 * 1 + q2 * 3 + i1 * 3 + -q0 * 1
*
* D_I1 = -q5 * 1 + -i4 * 3 + q3 * 3 + i2 * 1
* D_Q1 = i5 * 1 + -q4 * 3 + -i3 * 3 + q2 * 1
*/
// 6 cycles per complex input sample, not including loop overhead.
uint32_t q1_i0 = _q1_i0;
uint32_t q0_i1 = _q0_i1;
/* 3:1 Scaled by 32 to normalize output to +/-32768-ish. */
constexpr uint32_t scale_factor = 32;
const uint32_t k_3_1 = 0x00030001 * scale_factor;
uint32_t* src_p = reinterpret_cast<uint32_t*>(&src.p[0]);
uint32_t* const src_end = reinterpret_cast<uint32_t*>(&src.p[src.count]);
uint32_t* dst_p = reinterpret_cast<uint32_t*>(&dst.p[0]);
while(src_p < src_end) {
const uint32_t q3_i3_q2_i2 = *(src_p++); // 3
const uint32_t q5_i5_q4_i4 = *(src_p++);
const uint32_t i2_i3 = __SXTB16(q3_i3_q2_i2, 16); // 1: (q3_i3_q2_i2 ror 16)[23:16]:(q3_i3_q2_i2 ror 16)[7:0]
const uint32_t q3_q2 = __SXTB16(q3_i3_q2_i2, 8); // 1: (q3_i3_q2_i2 ror 8)[23:16]:(q3_i3_q2_i2 ror 8)[7:0]
const uint32_t i2_q3 = __PKHTB(i2_i3, q3_q2, 16); // 1: Rn[31:16]:(Rm>>16)[15:0]
const uint32_t i3_q2 = __PKHBT(q3_q2, i2_i3, 16); // 1:(Rm<<16)[31:16]:Rn[15:0]
// D_I0 = 3 * (i2 - q1) + (q3 - i0)
const uint32_t i2_m_q1_q3_m_i0 = __QSUB16(i2_q3, q1_i0); // 1: Rn[31:16]-Rm[31:16]:Rn[15:0]-Rm[15:0]
const uint32_t d_i0 = __SMUAD(k_3_1, i2_m_q1_q3_m_i0); // 1: Rm[15:0]*Rs[15:0]+Rm[31:16]*Rs[31:16]
// D_Q0 = 3 * (q2 + i1) - (i3 + q0)
const uint32_t i3_p_q0_q2_p_i1 = __QADD16(i3_q2, q0_i1); // 1: Rn[31:16]+Rm[31:16]:Rn[15:0]+Rm[15:0]
const uint32_t d_q0 = __SMUSDX(i3_p_q0_q2_p_i1, k_3_1); // 1: Rm[15:0]*Rs[31:16]Rm[31:16]*RsX[15:0]
const uint32_t d_q0_i0 = __PKHBT(d_i0, d_q0, 16); // 1: (Rm<<16)[31:16]:Rn[15:0]
const uint32_t i5_i4 = __SXTB16(q5_i5_q4_i4, 0); // 1: (q5_i5_q4_i4 ror 0)[23:16]:(q5_i5_q4_i4 ror 0)[7:0]
const uint32_t q4_q5 = __SXTB16(q5_i5_q4_i4, 24); // 1: (q5_i5_q4_i4 ror 24)[23:16]:(q5_i5_q4_i4 ror 24)[7:0]
const uint32_t q4_i5 = __PKHTB(q4_q5, i5_i4, 16); // 1: Rn[31:16]:(Rm>>16)[15:0]
const uint32_t q5_i4 = __PKHBT(i5_i4, q4_q5, 16); // 1: (Rm<<16)[31:16]:Rn[15:0]
// D_I1 = (i2 - q5) + 3 * (q3 - i4)
const uint32_t i2_m_q5_q3_m_i4 = __QSUB16(i2_q3, q5_i4); // 1: Rn[31:16]-Rm[31:16]:Rn[15:0]-Rm[15:0]
const uint32_t d_i1 = __SMUADX(i2_m_q5_q3_m_i4, k_3_1); // 1: Rm[15:0]*Rs[31:16]+Rm[31:16]*Rs[15:0]
// D_Q1 = (i5 + q2) - 3 * (q4 + i3)
const uint32_t q4_p_i3_i5_p_q2 = __QADD16(q4_i5, i3_q2); // 1: Rn[31:16]+Rm[31:16]:Rn[15:0]+Rm[15:0]
const uint32_t d_q1 = __SMUSD(k_3_1, q4_p_i3_i5_p_q2); // 1: Rm[15:0]*Rs[15:0]Rm[31:16]*Rs[31:16]
const uint32_t d_q1_i1 = __PKHBT(d_i1, d_q1, 16); // 1: (Rm<<16)[31:16]:Rn[15:0]
*(dst_p++) = d_q0_i0; // 3
*(dst_p++) = d_q1_i1;
q1_i0 = q5_i4;
q0_i1 = q4_i5;
}
_q1_i0 = q1_i0;
_q0_i1 = q0_i1;
return { dst.p, src.count / 2, src.sampling_rate / 2 };
}
buffer_c16_t DecimateBy2CIC3::execute(
buffer_c16_t src,
buffer_c16_t dst
) {
/* Complex non-recursive 3rd-order CIC filter (taps 1,3,3,1).
* Gain of 8.
* Consumes 16 bytes (4 s16:s16 samples) per loop iteration,
* Produces 8 bytes (2 s16:s16 samples) per loop iteration.
*/
uint32_t t1 = _iq0;
uint32_t t2 = _iq1;
uint32_t t3, t4;
const uint32_t taps = 0x00000003;
auto s = src.p;
auto d = dst.p;
const auto d_end = &dst.p[src.count / 2];
uint32_t i, q;
while(d < d_end) {
i = __SXTH(t1, 0); /* 1: I0 */
q = __SXTH(t1, 16); /* 1: Q0 */
i = __SMLABB(t2, taps, i); /* 1: I1*3 + I0 */
q = __SMLATB(t2, taps, q); /* 1: Q1*3 + Q0 */
t3 = *__SIMD32(s)++; /* 3: Q2:I2 */
t4 = *__SIMD32(s)++; /* Q3:I3 */
i = __SMLABB(t3, taps, i); /* 1: I2*3 + I1*3 + I0 */
q = __SMLATB(t3, taps, q); /* 1: Q2*3 + Q1*3 + Q0 */
int32_t si0 = __SXTAH(i, t4, 0); /* 1: I3 + Q2*3 + Q1*3 + Q0 */
int32_t sq0 = __SXTAH(q, t4, 16); /* 1: Q3 + Q2*3 + Q1*3 + Q0 */
i = __BFI(si0 / 8, sq0 / 8, 16, 16); /* 1: D2_Q0:D2_I0 */
*__SIMD32(d)++ = i; /* D2_Q0:D2_I0 */
i = __SXTH(t3, 0); /* 1: I2 */
q = __SXTH(t3, 16); /* 1: Q2 */
i = __SMLABB(t4, taps, i); /* 1: I3*3 + I2 */
q = __SMLATB(t4, taps, q); /* 1: Q3*3 + Q2 */
t1 = *__SIMD32(s)++; /* 3: Q4:I4 */
t2 = *__SIMD32(s)++; /* Q5:I5 */
i = __SMLABB(t1, taps, i); /* 1: I4*3 + I3*3 + I2 */
q = __SMLATB(t1, taps, q); /* 1: Q4*3 + Q3*3 + Q2 */
int32_t si1 = __SXTAH(i, t2, 0) ; /* 1: I5 + Q4*3 + Q3*3 + Q2 */
int32_t sq1 = __SXTAH(q, t2, 16); /* 1: Q5 + Q4*3 + Q3*3 + Q2 */
i = __BFI(si1 / 8, sq1 / 8, 16, 16); /* 1: D2_Q1:D2_I1 */
*__SIMD32(d)++ = i; /* D2_Q1:D2_I1 */
}
_iq0 = t1;
_iq1 = t2;
return { dst.p, src.count / 2, src.sampling_rate / 2 };
}
buffer_s16_t FIR64AndDecimateBy2Real::execute(
buffer_s16_t src,
buffer_s16_t dst
) {
/* int16_t input (sample count "n" must be multiple of 4)
* -> int16_t output, decimated by 2.
* taps are normalized to 1 << 16 == 1.0.
*/
auto src_p = src.p;
auto dst_p = dst.p;
int32_t n = src.count;
for(; n>0; n-=2) {
z[taps_count-2] = *(src_p++);
z[taps_count-1] = *(src_p++);
int32_t t = 0;
for(size_t j=0; j<taps_count; j+=4) {
t += z[j+0] * taps[j+0];
t += z[j+1] * taps[j+1];
t += z[j+2] * taps[j+2];
t += z[j+3] * taps[j+3];
z[j+0] = z[j+0+2];
z[j+1] = z[j+1+2];
z[j+2] = z[j+2+2];
z[j+3] = z[j+3+2];
}
*(dst_p++) = t / 65536;
}
return { dst.p, src.count / 2, src.sampling_rate / 2 };
}
#if 0
size_t fir_and_decimate_by_2_complex(
const complex16_t* const src_start,
const size_t src_count,
complex16_t* const dst_start,
complex16_t* const z,
const complex16_t* const taps,
const size_t taps_count
) {
/* int16_t input (sample count "n" must be multiple of 4)
* -> int16_t output, decimated by 2.
* taps are normalized to 1 << 16 == 1.0.
*/
auto src_p = src_start;
const auto src_end = &src_start[src_count];
auto dst_p = dst_start;
auto z_p = &z[0];
while(src_p < src_end) {
/* Put two new samples into delay buffer */
*__SIMD32(z_p)++ = *__SIMD32(src_p)++;
*__SIMD32(z_p)++ = *__SIMD32(src_p)++;
int64_t t_real = 0;
int64_t t_imag = 0;
auto t_p = &taps[0];
const auto z_end = &z[taps_count];
while(z_p < z_end) {
const auto tap0 = *__SIMD32(t_p)++;
const auto sample0 = *__SIMD32(z_p)++;
t_real = __SMLSLD(sample0, tap0, t_real);
t_imag = __SMLALDX(sample0, tap0, t_imag);
const auto tap1 = *__SIMD32(t_p)++;
const auto sample1 = *__SIMD32(z_p)++;
t_real = __SMLSLD(sample1, tap1, t_real);
t_imag = __SMLALDX(sample1, tap1, t_imag);
}
z_p = &z[0];
const auto t_end = &taps[taps_count];
while(t_p < t_end) {
const auto tap0 = *__SIMD32(t_p)++;
const auto sample0 = *__SIMD32(z_p)++;
t_real = __SMLSLD(sample0, tap0, t_real);
t_imag = __SMLALDX(sample0, tap0, t_imag);
const auto tap1 = *__SIMD32(t_p)++;
const auto sample1 = *__SIMD32(z_p)++;
t_real = __SMLSLD(sample1, tap1, t_real);
t_imag = __SMLALDX(sample1, tap1, t_imag);
}
if( z_p == z_end ) {
z_p = &z[0];
}
/* TODO: No rounding taking place here, so might be adding a bit of
* noise. Enough to be significant?
*/
*__SIMD32(dst_p)++ = __PKHBT(
t_real / 131072,
t_imag / 131072,
16
);
/*
*__SIMD32(dst_p)++ = __PKHBT(
__SSAT((t_real / 131072), 16),
__SSAT((t_imag / 131072), 16),
16
);
*/
}
return src_count / 2;
}
#endif
size_t fir_and_decimate_by_2_complex_fast(
const complex16_t* const src_start,
const size_t src_count,
complex16_t* const dst_start,
complex16_t* const z,
const complex16_t* const taps,
const size_t taps_count
) {
/* int16_t input (sample count "n" must be multiple of 4)
* -> int16_t output, decimated by 2.
* taps are normalized to 1 << 16 == 1.0.
*/
auto src_p = src_start;
auto dst_p = dst_start;
auto z_new_p = &z[0];
auto t_p = &taps[taps_count * 2];
while(src_p < &src_start[src_count]) {
/* Put two new samples into delay buffer */
*__SIMD32(z_new_p)++ = *__SIMD32(src_p)++;
*__SIMD32(z_new_p)++ = *__SIMD32(src_p)++;
t_p -= (taps_count + 2);
if( z_new_p == &z[taps_count] ) {
z_new_p = &z[0];
t_p = &taps[taps_count];
}
int64_t t_real = 0;
int64_t t_imag = 0;
auto z_p = &z[0];
while(z_p < &z[taps_count]) {
const auto tap0 = *__SIMD32(t_p)++;
const auto sample0 = *__SIMD32(z_p)++;
const auto tap1 = *__SIMD32(t_p)++;
const auto sample1 = *__SIMD32(z_p)++;
t_real = __SMLSLD(sample0, tap0, t_real);
t_imag = __SMLALDX(sample0, tap0, t_imag);
t_real = __SMLSLD(sample1, tap1, t_real);
t_imag = __SMLALDX(sample1, tap1, t_imag);
const auto tap2 = *__SIMD32(t_p)++;
const auto sample2 = *__SIMD32(z_p)++;
const auto tap3 = *__SIMD32(t_p)++;
const auto sample3 = *__SIMD32(z_p)++;
t_real = __SMLSLD(sample2, tap2, t_real);
t_imag = __SMLALDX(sample2, tap2, t_imag);
t_real = __SMLSLD(sample3, tap3, t_real);
t_imag = __SMLALDX(sample3, tap3, t_imag);
const auto tap4 = *__SIMD32(t_p)++;
const auto sample4 = *__SIMD32(z_p)++;
const auto tap5 = *__SIMD32(t_p)++;
const auto sample5 = *__SIMD32(z_p)++;
t_real = __SMLSLD(sample4, tap4, t_real);
t_imag = __SMLALDX(sample4, tap4, t_imag);
t_real = __SMLSLD(sample5, tap5, t_real);
t_imag = __SMLALDX(sample5, tap5, t_imag);
const auto tap6 = *__SIMD32(t_p)++;
const auto sample6 = *__SIMD32(z_p)++;
const auto tap7 = *__SIMD32(t_p)++;
const auto sample7 = *__SIMD32(z_p)++;
t_real = __SMLSLD(sample6, tap6, t_real);
t_imag = __SMLALDX(sample6, tap6, t_imag);
t_real = __SMLSLD(sample7, tap7, t_real);
t_imag = __SMLALDX(sample7, tap7, t_imag);
}
/* TODO: Re-evaluate whether saturation is performed, normalization,
* all that jazz.
*/
const int32_t r = t_real >> 16;
const int32_t i = t_imag >> 16;
const int32_t r_sat = __SSAT(r, 16);
const int32_t i_sat = __SSAT(i, 16);
*__SIMD32(dst_p)++ = __PKHBT(
r_sat,
i_sat,
16
);
}
return src_count / 2;
}
buffer_s16_t DecimateBy2CIC4Real::execute(
buffer_s16_t src,
buffer_s16_t dst
) {
auto src_p = src.p;
auto dst_p = dst.p;
int32_t n = src.count;
for(; n>0; n-=2) {
/* TODO: Probably a lot of room to optimize... */
z[0] = z[2];
z[1] = z[3];
z[2] = z[4];
z[3] = *(src_p++);
z[4] = *(src_p++);
int32_t t = z[0] + z[1] * 4 + z[2] * 6 + z[3] * 4 + z[4];
*(dst_p++) = t / 16;
}
return { dst.p, src.count / 2, src.sampling_rate / 2 };
}
#if 0
buffer_c16_t DecimateBy2HBF5Complex::execute(
buffer_c16_t const src,
buffer_c16_t const dst
) {
auto src_p = src.p;
auto dst_p = dst.p;
int32_t n = src.count;
for(; n>0; n-=2) {
/* TODO: Probably a lot of room to optimize... */
z[0] = z[2];
//z[1] = z[3];
z[2] = z[4];
//z[3] = z[5];
z[4] = z[6];
z[5] = z[7];
z[6] = z[8];
z[7] = z[9];
z[8] = z[10];
z[9] = *(src_p++);
z[10] = *(src_p++);
int32_t t_real { z[5].real * 256 };
int32_t t_imag { z[5].imag * 256 };
t_real += (z[ 0].real + z[10].real) * 3;
t_imag += (z[ 0].imag + z[10].imag) * 3;
t_real -= (z[ 2].real + z[ 8].real) * 25;
t_imag -= (z[ 2].imag + z[ 8].imag) * 25;
t_real += (z[ 4].real + z[ 6].real) * 150;
t_imag += (z[ 4].imag + z[ 6].imag) * 150;
*(dst_p++) = { t_real / 256, t_imag / 256 };
}
return { dst.p, src.count / 2, src.sampling_rate / 2 };
}
buffer_c16_t DecimateBy2HBF7Complex::execute(
buffer_c16_t const src,
buffer_c16_t const dst
) {
auto src_p = src.p;
auto dst_p = dst.p;
int32_t n = src.count;
for(; n>0; n-=2) {
/* TODO: Probably a lot of room to optimize... */
z[0] = z[2];
//z[1] = z[3];
z[2] = z[4];
//z[3] = z[5];
z[4] = z[6];
z[5] = z[7];
z[6] = z[8];
z[7] = z[9];
z[8] = z[10];
z[9] = *(src_p++);
z[10] = *(src_p++);
int32_t t_real { z[5].real * 512 };
int32_t t_imag { z[5].imag * 512 };
t_real += (z[ 0].real + z[10].real) * 7;
t_imag += (z[ 0].imag + z[10].imag) * 7;
t_real -= (z[ 2].real + z[ 8].real) * 53;
t_imag -= (z[ 2].imag + z[ 8].imag) * 53;
t_real += (z[ 4].real + z[ 6].real) * 302;
t_imag += (z[ 4].imag + z[ 6].imag) * 302;
*(dst_p++) = { t_real / 512, t_imag / 512 };
}
return { dst.p, src.count / 2, src.sampling_rate / 2 };
}
#endif
} /* namespace decimate */
} /* namespace dsp */

View File

@ -0,0 +1,242 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __DSP_DECIMATE_H__
#define __DSP_DECIMATE_H__
#include <cstdint>
#include <array>
#include "dsp_types.hpp"
namespace dsp {
namespace decimate {
class TranslateByFSOver4AndDecimateBy2CIC3 {
public:
buffer_c16_t execute(
buffer_c8_t src,
buffer_c16_t dst
);
private:
uint32_t _q1_i0 { 0 };
uint32_t _q0_i1 { 0 };
};
class DecimateBy2CIC3 {
public:
buffer_c16_t execute(
buffer_c16_t src,
buffer_c16_t dst
);
private:
uint32_t _iq0 { 0 };
uint32_t _iq1 { 0 };
};
class FIR64AndDecimateBy2Real {
public:
static constexpr size_t taps_count = 64;
FIR64AndDecimateBy2Real(
const std::array<int16_t, taps_count>& taps
) : taps { taps }
{
}
buffer_s16_t execute(
buffer_s16_t src,
buffer_s16_t dst
);
private:
std::array<int16_t, taps_count + 2> z;
const std::array<int16_t, taps_count>& taps;
};
size_t fir_and_decimate_by_2_complex(
const complex16_t* const src_start,
const size_t src_count,
complex16_t* const dst_start,
complex16_t* const z,
const complex16_t* const taps,
const size_t taps_count
);
size_t fir_and_decimate_by_2_complex_fast(
const complex16_t* const src_start,
const size_t src_count,
complex16_t* const dst_start,
complex16_t* const z,
const complex16_t* const taps,
const size_t taps_count
);
template<size_t taps_count>
class FIRAndDecimateBy2Complex {
public:
/* NOTE! Current code makes an assumption that block of samples to be
* processed will be a multiple of the taps_count.
*/
FIRAndDecimateBy2Complex(
const std::array<int16_t, taps_count>& real_taps
) {
for(size_t i=0; i<taps_count; i++) {
taps[ i] = real_taps[i];
taps[taps_count + i] = real_taps[i];
}
}
buffer_c16_t execute(
buffer_c16_t src,
buffer_c16_t dst
) {
const auto dst_count = fir_and_decimate_by_2_complex_fast(src.p, src.count, dst.p, z.data(), taps.data(), taps_count);
return { dst.p, dst_count, src.sampling_rate / 2 };
}
private:
std::array<complex16_t, taps_count * 2> taps;
std::array<complex16_t, taps_count> z;
};
class DecimateBy2CIC4Real {
public:
buffer_s16_t execute(
buffer_s16_t src,
buffer_s16_t dst
);
private:
int16_t z[5];
};
#if 0
class DecimateBy2HBF5Complex {
public:
buffer_c16_t execute(
buffer_c16_t const src,
buffer_c16_t const dst
);
private:
complex16_t z[11];
};
class DecimateBy2HBF7Complex {
public:
buffer_c16_t execute(
buffer_c16_t const src,
buffer_c16_t const dst
);
private:
complex16_t z[11];
};
#endif
/* From http://www.dspguru.com/book/export/html/3
Here are several basic techniques to fake circular buffers:
Split the calculation: You can split any FIR calculation into its "pre-wrap"
and "post-wrap" parts. By splitting the calculation into these two parts, you
essentially can do the circular logic only once, rather than once per tap.
(See fir_double_z in FirAlgs.c above.)
Duplicate the delay line: For a FIR with N taps, use a delay line of size 2N.
Copy each sample to its proper location, as well as at location-plus-N.
Therefore, the FIR calculation's MAC loop can be done on a flat buffer of N
points, starting anywhere within the first set of N points. The second set of
N delayed samples provides the "wrap around" comparable to a true circular
buffer. (See fir_double_z in FirAlgs.c above.)
Duplicate the coefficients: This is similar to the above, except that the
duplication occurs in terms of the coefficients, not the delay line.
Compared to the previous method, this has a calculation advantage of not
having to store each incoming sample twice, and it also has a memory
advantage when the same coefficient set will be used on multiple delay lines.
(See fir_double_h in FirAlgs.c above.)
Use block processing: In block processing, you use a delay line which is a
multiple of the number of taps. You therefore only have to move the data
once per block to implement the delay-line mechanism. When the block size
becomes "large", the overhead of a moving the delay line once per block
becomes negligible.
*/
#if 0
template<size_t N>
class FIRAndDecimateBy2Complex {
public:
FIR64AndDecimateBy2Complex(
const std::array<int16_t, N>& taps
) : taps { taps }
{
}
buffer_c16_t execute(
buffer_c16_t const src,
buffer_c16_t const dst
) {
/* int16_t input (sample count "n" must be multiple of 4)
* -> int16_t output, decimated by 2.
* taps are normalized to 1 << 16 == 1.0.
*/
return { dst.p, src.count / 2 };
}
private:
std::array<complex16_t, N> z;
const std::array<int16_t, N>& taps;
complex<int16_t> process_one(const size_t start_offset) {
const auto split = &z[start_offset];
const auto end = &z[z.size()];
auto tap = &taps[0];
complex<int32_t> t { 0, 0 };
auto p = split;
while(p < end) {
const auto t = *(tap++);
const auto c = *(p++);
t.real += c.real * t;
t.imag += c.imag * t;
}
p = &z[0];
while(p < split) {
const auto t = *(tap++);
const auto c = *(p++);
t.real += c.real * t;
t.imag += c.imag * t;
}
return { t.real / 65536, t.imag / 65536 };
}
};
#endif
} /* namespace decimate */
} /* namespace dsp */
#endif/*__DSP_DECIMATE_H__*/

View File

@ -0,0 +1,110 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "dsp_demodulate.hpp"
#include "complex.hpp"
#include "fxpt_atan2.hpp"
#include <hal.h>
namespace dsp {
namespace demodulate {
buffer_s16_t AM::execute(
buffer_c16_t src,
buffer_s16_t dst
) {
/* Intermediate maximum value: 46341 (when input is -32768,-32768). */
/* Normalized to maximum 32767 for int16_t representation. */
const auto src_p = src.p;
const auto src_end = &src.p[src.count];
auto dst_p = dst.p;
while(src_p < src_end) {
// const auto s = *(src_p++);
// const uint32_t r_sq = s.real() * s.real();
// const uint32_t i_sq = s.imag() * s.imag();
// const uint32_t mag_sq = r_sq + i_sq;
const uint32_t sample0 = *__SIMD32(src_p)++;
const uint32_t sample1 = *__SIMD32(src_p)++;
const uint32_t mag_sq0 = __SMUAD(sample0, sample0);
const uint32_t mag_sq1 = __SMUAD(sample1, sample1);
const int32_t mag0_int = __builtin_sqrtf(mag_sq0);
const int32_t mag0_sat = __SSAT(mag0_int, 16);
const int32_t mag1_int = __builtin_sqrtf(mag_sq1);
const int32_t mag1_sat = __SSAT(mag1_int, 16);
*__SIMD32(dst_p)++ = __PKHBT(
mag0_sat,
mag1_sat,
16
);
}
return { dst.p, src.count, src.sampling_rate };
}
/*
static inline float angle_approx_4deg0(const complex32_t t) {
const auto x = static_cast<float>(t.imag()) / static_cast<float>(t.real());
return 16384.0f * x;
}
*/
static inline float angle_approx_0deg27(const complex32_t t) {
const auto x = static_cast<float>(t.imag()) / static_cast<float>(t.real());
return x / (1.0f + 0.28086f * x * x);
}
/*
static inline float angle_precise(const complex32_t t) {
return atan2f(t.imag(), t.real());
}
*/
buffer_s16_t FM::execute(
buffer_c16_t src,
buffer_s16_t dst
) {
auto z = z_;
const auto src_p = src.p;
const auto src_end = &src.p[src.count];
auto dst_p = dst.p;
while(src_p < src_end) {
const auto s0 = *__SIMD32(src_p)++;
const auto s1 = *__SIMD32(src_p)++;
const auto t0 = multiply_conjugate_s16_s32(s0, z);
const auto t1 = multiply_conjugate_s16_s32(s1, s0);
z = s1;
const int32_t theta0_int = angle_approx_0deg27(t0) * k;
const int32_t theta0_sat = __SSAT(theta0_int, 16);
const int32_t theta1_int = angle_approx_0deg27(t1) * k;
const int32_t theta1_sat = __SSAT(theta1_int, 16);
*__SIMD32(dst_p)++ = __PKHBT(
theta0_sat,
theta1_sat,
16
);
}
z_ = z;
return { dst.p, src.count, src.sampling_rate };
}
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __DSP_DEMODULATE_H__
#define __DSP_DEMODULATE_H__
#include "dsp_types.hpp"
namespace dsp {
namespace demodulate {
class AM {
public:
buffer_s16_t execute(
buffer_c16_t src,
buffer_s16_t dst
);
};
class FM {
public:
/*
* angle: -pi to pi. output range: -32768 to 32767.
* Maximum delta-theta (output of atan2) at maximum deviation frequency:
* delta_theta_max = 2 * pi * deviation / sampling_rate
*/
constexpr FM(
const float sampling_rate,
const float deviation_hz
) : z_ { 0 },
k { static_cast<float>(32767.0f / (2.0 * pi * deviation_hz / sampling_rate)) }
{
}
buffer_s16_t execute(
buffer_c16_t src,
buffer_s16_t dst
);
private:
complex16_t::rep_type z_;
const float k;
};
} /* namespace demodulate */
} /* namespace dsp */
#endif/*__DSP_DEMODULATE_H__*/

View File

@ -0,0 +1,22 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "dsp_fir_taps.hpp"

View File

@ -0,0 +1,285 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __DSP_FIR_TAPS_H__
#define __DSP_FIR_TAPS_H__
#include <cstdint>
#include <array>
#include "complex.hpp"
/* 3kHz/6.7kHz @ 96kHz. sum(abs(taps)): 89429 */
constexpr std::array<int16_t, 64> taps_64_lp_031_070_tfilter { {
56, 58, 81, 100, 113, 112, 92, 49,
-21, -120, -244, -389, -543, -692, -819, -903,
-923, -861, -698, -424, -34, 469, 1073, 1756,
2492, 3243, 3972, 4639, 5204, 5634, 5903, 5995,
5903, 5634, 5204, 4639, 3972, 3243, 2492, 1756,
1073, 469, -34, -424, -698, -861, -923, -903,
-819, -692, -543, -389, -244, -120, -21, 49,
92, 112, 113, 100, 81, 58, 56, 0,
} };
/* 4kHz/7.5kHz @ 96kHz. sum(abs(taps)): 96783 */
constexpr std::array<int16_t, 64> taps_64_lp_042_078_tfilter { {
-19, 39, 72, 126, 197, 278, 360, 432,
478, 485, 438, 327, 152, -82, -359, -651,
-922, -1132, -1236, -1192, -968, -545, 81, 892,
1852, 2906, 3984, 5012, 5910, 6609, 7053, 7205,
7053, 6609, 5910, 5012, 3984, 2906, 1852, 892,
81, -545, -968, -1192, -1236, -1132, -922, -651,
-359, -82, 152, 327, 438, 485, 478, 432,
360, 278, 197, 126, 72, 39, -19, 0,
} };
/* 5kHz/8.5kHz @ 96kHz. sum(abs(taps)): 101312 */
constexpr std::array<int16_t, 64> taps_64_lp_052_089_tfilter { {
-65, -88, -129, -163, -178, -160, -100, 9,
160, 340, 523, 675, 758, 738, 591, 313,
-76, -533, -987, -1355, -1544, -1472, -1077, -335,
738, 2078, 3579, 5104, 6502, 7627, 8355, 8608,
8355, 7627, 6502, 5104, 3579, 2078, 738, -335,
-1077, -1472, -1544, -1355, -987, -533, -76, 313,
591, 738, 758, 675, 523, 340, 160, 9,
-100, -160, -178, -163, -129, -88, -65, 0,
} };
/* 6kHz/9.6kHz @ 96kHz. sum(abs(taps)): 105088 */
constexpr std::array<int16_t, 64> taps_64_lp_063_100_tfilter { {
43, 21, -2, -54, -138, -245, -360, -453,
-493, -451, -309, -73, 227, 535, 776, 876,
773, 443, -86, -730, -1357, -1801, -1898, -1515,
-585, 869, 2729, 4794, 6805, 8490, 9611, 10004,
9611, 8490, 6805, 4794, 2729, 869, -585, -1515,
-1898, -1801, -1357, -730, -86, 443, 773, 876,
776, 535, 227, -73, -309, -451, -493, -453,
-360, -245, -138, -54, -2, 21, 43, 0,
} };
/* 7kHz/10.4kHz @ 96kHz: sum(abs(taps)): 110157 */
constexpr std::array<int16_t, 64> taps_64_lp_073_108_tfilter { {
79, 145, 241, 334, 396, 394, 306, 130,
-109, -360, -550, -611, -494, -197, 229, 677,
1011, 1096, 846, 257, -570, -1436, -2078, -2225,
-1670, -327, 1726, 4245, 6861, 9146, 10704, 11257,
10704, 9146, 6861, 4245, 1726, -327, -1670, -2225,
-2078, -1436, -570, 257, 846, 1096, 1011, 677,
229, -197, -494, -611, -550, -360, -109, 130,
306, 394, 396, 334, 241, 145, 79, 0,
} };
/* 8kHz/11.5kHz @ 96kHz. sum(abs(taps)): 112092 */
constexpr std::array<int16_t, 64> taps_64_lp_083_120_tfilter { {
-63, -72, -71, -21, 89, 248, 417, 537,
548, 407, 124, -237, -563, -723, -621, -238,
337, 919, 1274, 1201, 617, -382, -1514, -2364,
-2499, -1600, 414, 3328, 6651, 9727, 11899, 12682,
11899, 9727, 6651, 3328, 414, -1600, -2499, -2364,
-1514, -382, 617, 1201, 1274, 919, 337, -238,
-621, -723, -563, -237, 124, 407, 548, 537,
417, 248, 89, -21, -71, -72, -63, 0,
} };
/* 9kHz/12.4kHz @ 96kHz. sum(abs(taps)): 116249 */
constexpr std::array<int16_t, 64> taps_64_lp_094_129_tfilter { {
5, -93, -198, -335, -449, -478, -378, -144,
166, 444, 563, 440, 82, -395, -788, -892,
-589, 73, 859, 1421, 1431, 734, -530, -1919,
-2798, -2555, -837, 2274, 6220, 10103, 12941, 13981,
12941, 10103, 6220, 2274, -837, -2555, -2798, -1919,
-530, 734, 1431, 1421, 859, 73, -589, -892,
-788, -395, 82, 440, 563, 444, 166, -144,
-378, -478, -449, -335, -198, -93, 5, 0,
} };
/* 10kHz/13.4kHz @ 96kHz. sum(abs(taps)): 118511 */
constexpr std::array<int16_t, 64> taps_64_lp_104_140_tfilter { {
89, 159, 220, 208, 84, -147, -412, -597,
-588, -345, 58, 441, 595, 391, -128, -730,
-1080, -914, -198, 793, 1558, 1594, 678, -942,
-2546, -3187, -2084, 992, 5515, 10321, 13985, 15353,
13985, 10321, 5515, 992, -2084, -3187, -2546, -942,
678, 1594, 1558, 793, -198, -914, -1080, -730,
-128, 391, 595, 441, 58, -345, -588, -597,
-412, -147, 84, 208, 220, 159, 89, 0,
} };
/* Wideband FM channel filter
* 103kHz/128kHz @ 768kHz
*/
constexpr std::array<int16_t, 64> taps_64_lp_130_169_tfilter { {
100, 127, 62, -157, -470, -707, -678, -332,
165, 494, 400, -85, -610, -729, -253, 535,
1026, 734, -263, -1264, -1398, -332, 1316, 2259,
1447, -988, -3474, -3769, -385, 6230, 13607, 18450,
18450, 13607, 6230, -385, -3769, -3474, -988, 1447,
2259, 1316, -332, -1398, -1264, -263, 734, 1026,
535, -253, -729, -610, -85, 400, 494, 165,
-332, -678, -707, -470, -157, 62, 127, 100,
} };
/* Wideband audio filter */
/* 96kHz int16_t input
* -> FIR filter, <15kHz (0.156fs) pass, >19kHz (0.198fs) stop
* -> 48kHz int16_t output, gain of 1.0 (I think).
* Padded to multiple of four taps for unrolled FIR code.
* sum(abs(taps)): 125270
*/
constexpr std::array<int16_t, 64> taps_64_lp_156_198 { {
-27, 166, 104, -36, -174, -129, 109, 287,
148, -232, -430, -130, 427, 597, 49, -716,
-778, 137, 1131, 957, -493, -1740, -1121, 1167,
2733, 1252, -2633, -4899, -1336, 8210, 18660, 23254,
18660, 8210, -1336, -4899, -2633, 1252, 2733, 1167,
-1121, -1740, -493, 957, 1131, 137, -778, -716,
49, 597, 427, -130, -430, -232, 148, 287,
109, -129, -174, -36, 104, 166, -27, 0,
} };
/* Narrowband FM filter */
/* 96kHz int16_t input
* -> FIR filter, <4kHz (0.042fs) pass, Hamming window.
* -> 48kHz int16_t output, gain of 1.0.
* Padded to multiple of four taps for unrolled FIR code.
* sum(abs(taps)): 81493
*/
constexpr std::array<int16_t, 64> taps_64_lp_042_hamming { {
51, 57, 61, 65, 63, 55, 35, 0,
-52, -122, -207, -304, -402, -491, -556, -582,
-552, -453, -271, 0, 363, 815, 1343, 1930,
2553, 3183, 3788, 4338, 4802, 5154, 5374, 5449,
5374, 5154, 4802, 4338, 3788, 3183, 2553, 1930,
1343, 815, 363, 0, -271, -453, -552, -582,
-556, -491, -402, -304, -207, -122, -52, 0,
35, 55, 63, 65, 61, 57, 51, 0
} };
/* Narrowband FM filter */
/* 96kHz int16_t input
* -> FIR filter, <6kHz (0.063fs) pass, Hamming window.
* -> 48kHz int16_t output, gain of 1.0.
* Padded to multiple of four taps for unrolled FIR code.
* sum(abs(taps)): 92477
*/
constexpr std::array<int16_t, 64> taps_64_lp_063_hamming { {
-20, -40, -59, -75, -83, -78, -52, 0,
77, 173, 272, 352, 386, 348, 221, 0,
-300, -644, -974, -1219, -1304, -1158, -730, 0,
1016, 2261, 3641, 5034, 6305, 7325, 7985, 8213,
7985, 7325, 6305, 5034, 3641, 2261, 1016, 0,
-730, -1158, -1304, -1219, -974, -644, -300, 0,
221, 348, 386, 352, 272, 173, 77, 0,
-52, -78, -83, -75, -59, -40, -20, 0
} };
/* sum(abs(taps)): 85241 */
constexpr std::array<int16_t, 64> taps_64_lp_063_blackman { {
0, 0, -2, -6, -11, -14, -11, 0,
24, 62, 110, 157, 188, 184, 125, 0,
-194, -441, -704, -926, -1035, -957, -626, 0,
925, 2109, 3466, 4872, 6182, 7249, 7946, 8189,
7946, 7249, 6182, 4872, 3466, 2109, 925, 0,
-626, -957, -1035, -926, -704, -441, -194, 0,
125, 184, 188, 157, 110, 62, 24, 0,
-11, -14, -11, -6, -2, 0, 0, 0
} };
/* Narrowband FM filter */
/* 96kHz int16_t input
* -> FIR filter, <8kHz (0.083fs) pass, Hamming window.
* -> 48kHz int16_t output, gain of 1.0.
* Padded to multiple of four taps for unrolled FIR code.
* sum(abs(taps)): 99180
*/
constexpr std::array<int16_t, 64> taps_64_lp_083_hamming { {
-26, 0, 32, 65, 90, 95, 67, 0,
-100, -211, -294, -304, -208, 0, 288, 582,
782, 785, 524, 0, -702, -1412, -1901, -1931,
-1322, 0, 1962, 4340, 6795, 8932, 10388, 10904,
10388, 8932, 6795, 4340, 1962, 0, -1322, -1931,
-1901, -1412, -702, 0, 524, 785, 782, 582,
288, 0, -208, -304, -294, -211, -100, 0,
67, 95, 90, 65, 32, 0, -26, 0
} };
/* sum(abs(taps)): 92568 */
constexpr std::array<int16_t, 64> taps_64_lp_083_blackman { {
0, 0, 1, 5, 12, 17, 15, 0,
-32, -77, -119, -136, -101, 0, 164, 355,
508, 541, 381, 0, -560, -1173, -1636, -1717,
-1209, 0, 1876, 4221, 6695, 8883, 10388, 10924,
10388, 8883, 6695, 4221, 1876, 0, -1209, -1717,
-1636, -1173, -560, 0, 381, 541, 508, 355,
164, 0, -101, -136, -119, -77, -32, 0,
15, 17, 12, 5, 1, 0, 0, 0
} };
/* Narrowband FM filter */
/* 96kHz int16_t input
* -> FIR filter, <10kHz (0.104fs) pass, Hamming window.
* -> 48kHz int16_t output, gain of 1.0.
* Padded to multiple of four taps for unrolled FIR code.
* sum(abs(taps)): 106396
*/
constexpr std::array<int16_t, 64> taps_64_lp_104_hamming { {
53, 40, 8, -37, -83, -106, -82, 0,
123, 236, 272, 175, -54, -347, -571, -583,
-299, 235, 833, 1215, 1116, 422, -728, -1934,
-2625, -2254, -512, 2509, 6285, 9975, 12663, 13646,
12663, 9975, 6285, 2509, -512, -2254, -2625, -1934,
-728, 422, 1116, 1215, 833, 235, -299, -583,
-571, -347, -54, 175, 272, 236, 123, 0,
-82, -106, -83, -37, 8, 40, 53, 0
} };
/* sum(abs(taps)): 99426 */
constexpr std::array<int16_t, 64> taps_64_lp_104_blackman { {
0, 0, 0, -3, -11, -19, -18, 0,
39, 85, 110, 78, -26, -184, -325, -355,
-194, 161, 605, 926, 889, 350, -626, -1717,
-2397, -2109, -489, 2436, 6184, 9906, 12645, 13652,
12645, 9906, 6184, 2436, -489, -2109, -2397, -1717,
-626, 350, 889, 926, 605, 161, -194, -355,
-325, -184, -26, 78, 110, 85, 39, 0,
-18, -19, -11, -3, 0, 0, 0, 0
} };
/* Narrowband AM audio filter */
/* 96kHz int16_t input
* -> FIR filter, <3kHz (0.031fs) pass, >6kHz (0.063fs) stop
* -> 48kHz int16_t output, gain of 1.0 (I think).
* Padded to multiple of four taps for unrolled FIR code.
* sum(abs(taps)): 94053
*/
/* TODO: Review this filter, it's very quick and dirty. */
constexpr std::array<int16_t, 64> taps_64_lp_031_063 { {
-254, 255, 244, 269, 302, 325, 325, 290,
215, 99, -56, -241, -442, -643, -820, -950,
-1009, -974, -828, -558, -160, 361, 992, 1707,
2477, 3264, 4027, 4723, 5312, 5761, 6042, 6203,
6042, 5761, 5312, 4723, 4027, 3264, 2477, 1707,
992, 361, -160, -558, -828, -974, -1009, -950,
-820, -643, -442, -241, -56, 99, 215, 290,
325, 325, 302, 269, 244, 255, -254, 0,
} };
#endif/*__DSP_FIR_TAPS_H__*/

View File

@ -0,0 +1,68 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __DSP_IIR_H__
#define __DSP_IIR_H__
#include <array>
#include "dsp_types.hpp"
class IIRBiquadFilter {
public:
// http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
// Assume all coefficients are normalized so that a0=1.0
constexpr IIRBiquadFilter(
std::array<float, 3> b,
std::array<float, 3> a
) : b(b),
a(a)
{
}
void execute(buffer_s16_t buffer) {
for(size_t i=0; i<buffer.count; i++) {
buffer.p[i] = execute_sample(buffer.p[i]);
}
}
private:
const std::array<float, 3> b;
const std::array<float, 3> a;
std::array<float, 3> x { { 0.0f, 0.0f, 0.0f } };
std::array<float, 3> y { { 0.0f, 0.0f, 0.0f } };
float execute_sample(const float in) {
x[0] = x[1];
x[1] = x[2];
x[2] = in;
y[0] = y[1];
y[1] = y[2];
y[2] = b[0] * x[2] + b[1] * x[1] + b[2] * x[0]
- a[1] * y[1] - a[2] * y[0];
return y[2];
}
};
#endif/*__DSP_IIR_H__*/

View File

@ -0,0 +1,30 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "event_m4.hpp"
#include "ch.h"
Thread* thread_event_loop = nullptr;
void events_initialize(Thread* const event_loop_thread) {
thread_event_loop = event_loop_thread;
}

View File

@ -0,0 +1,46 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __EVENT_M4_H__
#define __EVENT_M4_H__
#include "ch.h"
constexpr auto EVT_MASK_BASEBAND = EVENT_MASK(0);
constexpr auto EVT_MASK_SPECTRUM = EVENT_MASK(1);
void events_initialize(Thread* const event_loop_thread);
extern Thread* thread_event_loop;
inline void events_flag(const eventmask_t events) {
if( thread_event_loop ) {
chEvtSignal(thread_event_loop, events);
}
}
inline void events_flag_isr(const eventmask_t events) {
if( thread_event_loop ) {
chEvtSignalI(thread_event_loop, events);
}
}
#endif/*__EVENT_M4_H__*/

View File

@ -0,0 +1,152 @@
/*
* fxpt_atan2.c
*
* Copyright (C) 2012, Xo Wang
*
* Hacked up to be a bit more ARM-friendly by:
* Copyright (C) 2013 Jared Boone, ShareBrained Technology, Inc.
*
* 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 <math.h>
#include <stdint.h>
#include <stdlib.h>
/**
* Convert floating point to Q15 (1.0.15 fixed point) format.
*
* @param d floating-point value within range -1 to (1 - (2**-15)), inclusive
* @return Q15 value representing d; same range
*/
/*
static inline int16_t q15_from_double(const double d) {
return lrint(d * 32768);
}
*/
/**
* Negative absolute value. Used to avoid undefined behavior for most negative
* integer (see C99 standard 7.20.6.1.2 and footnote 265 for the description of
* abs/labs/llabs behavior).
*
* @param i 16-bit signed integer
* @return negative absolute value of i; defined for all values of i
*/
/*
static inline int16_t s16_nabs(const int16_t j) {
#if (((int16_t)-1) >> 1) == ((int16_t)-1)
// signed right shift sign-extends (arithmetic)
const int16_t negSign = ~(j >> 15); // splat sign bit into all 16 and complement
// if j is positive (negSign is -1), xor will invert j and sub will add 1
// otherwise j is unchanged
return (j ^ negSign) - negSign;
#else
return (j < 0 ? j : -j);
#endif
}
*/
/**
* Q15 (1.0.15 fixed point) multiplication. Various common rounding modes are in
* the function definition for reference (and preference).
*
* @param j 16-bit signed integer representing -1 to (1 - (2**-15)), inclusive
* @param k same format as j
* @return product of j and k, in same format
*/
static inline int16_t q15_mul(const int16_t j, const int16_t k) {
const int32_t intermediate = j * k;
#if 0 // don't round
return intermediate >> 15;
#elif 0 // biased rounding
return (intermediate + 0x4000) >> 15;
#else // unbiased rounding
return (intermediate + ((intermediate & 0x7FFF) == 0x4000 ? 0 : 0x4000)) >> 15;
#endif
}
/**
* Q15 (1.0.15 fixed point) division (non-saturating). Be careful when using
* this function, as it does not behave well when the result is out-of-range.
*
* Value is not defined if numerator is greater than or equal to denominator.
*
* @param numer 16-bit signed integer representing -1 to (1 - (2**-15))
* @param denom same format as numer; must be greater than numerator
* @return numer / denom in same format as numer and denom
*/
static inline int16_t q15_div(const int16_t numer, const int16_t denom) {
return (static_cast<int32_t>(numer) << 15) / denom;
}
/**
* 16-bit fixed point four-quadrant arctangent. Given some Cartesian vector
* (x, y), find the angle subtended by the vector and the positive x-axis.
*
* The value returned is in units of 1/65536ths of one turn. This allows the use
* of the full 16-bit unsigned range to represent a turn. e.g. 0x0000 is 0
* radians, 0x8000 is pi radians, and 0xFFFF is (65535 / 32768) * pi radians.
*
* Because the magnitude of the input vector does not change the angle it
* represents, the inputs can be in any signed 16-bit fixed-point format.
*
* @param y y-coordinate in signed 16-bit
* @param x x-coordinate in signed 16-bit
* @return angle in (val / 32768) * pi radian increments from 0x0000 to 0xFFFF
*/
static inline int16_t nabs(const int16_t j) {
//return -abs(x);
return (j < 0 ? j : -j);
}
int16_t fxpt_atan2(const int16_t y, const int16_t x) {
static const int16_t k1 = 2847;
static const int16_t k2 = 11039;
if (x == y) { // x/y or y/x would return -1 since 1 isn't representable
if (y > 0) { // 1/8
return 8192;
} else if (y < 0) { // 5/8
return 40960;
} else { // x = y = 0
return 0;
}
}
const int16_t nabs_y = nabs(y);
const int16_t nabs_x = nabs(x);
if (nabs_x < nabs_y) { // octants 1, 4, 5, 8
const int16_t y_over_x = q15_div(y, x);
const int16_t correction = q15_mul(k1, nabs(y_over_x));
const int16_t unrotated = q15_mul(k2 + correction, y_over_x);
if (x > 0) { // octants 1, 8
return unrotated;
} else { // octants 4, 5
return 32768 + unrotated;
}
} else { // octants 2, 3, 6, 7
const int16_t x_over_y = q15_div(x, y);
const int16_t correction = q15_mul(k1, nabs(x_over_y));
const int16_t unrotated = q15_mul(k2 + correction, x_over_y);
if (y > 0) { // octants 2, 3
return 16384 - unrotated;
} else { // octants 6, 7
return 49152 - unrotated;
}
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __FXPT_ATAN2_H__
#define __FXPT_ATAN2_H__
#include <cstdint>
int16_t fxpt_atan2(const int16_t y, const int16_t x);
#endif/*__FXPT_ATAN2_H__*/

Some files were not shown because too many files have changed in this diff Show More