mirror of
https://github.com/eried/portapack-mayhem.git
synced 2024-10-01 01:26:06 -04:00
Initial firmware commit.
This commit is contained in:
parent
626e863257
commit
dc6fee8370
296
firmware/application/Makefile
Executable file
296
firmware/application/Makefile
Executable 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
|
51
firmware/application/baseband_cpld.cpp
Normal file
51
firmware/application/baseband_cpld.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
41
firmware/application/baseband_cpld.hpp
Normal file
41
firmware/application/baseband_cpld.hpp
Normal 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__*/
|
373
firmware/application/baseband_sgpio.cpp
Normal file
373
firmware/application/baseband_sgpio.cpp
Normal 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 */
|
67
firmware/application/baseband_sgpio.hpp
Normal file
67
firmware/application/baseband_sgpio.hpp
Normal 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
543
firmware/application/chconf.h
Executable 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_ */
|
||||
|
||||
/** @} */
|
540
firmware/application/clock_manager.cpp
Normal file
540
firmware/application/clock_manager.cpp
Normal 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
|
||||
);
|
||||
}
|
87
firmware/application/clock_manager.hpp
Normal file
87
firmware/application/clock_manager.hpp
Normal 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__*/
|
84
firmware/application/cpld_update.cpp
Normal file
84
firmware/application/cpld_update.cpp
Normal 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;
|
||||
}
|
27
firmware/application/cpld_update.hpp
Normal file
27
firmware/application/cpld_update.hpp
Normal 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__*/
|
39
firmware/application/debounce.cpp
Normal file
39
firmware/application/debounce.cpp
Normal 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;
|
||||
}
|
40
firmware/application/debounce.hpp
Normal file
40
firmware/application/debounce.hpp
Normal 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__*/
|
64
firmware/application/dirty_registers.hpp
Normal file
64
firmware/application/dirty_registers.hpp
Normal 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__*/
|
55
firmware/application/encoder.cpp
Normal file
55
firmware/application/encoder.cpp
Normal 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];
|
||||
}
|
38
firmware/application/encoder.hpp
Normal file
38
firmware/application/encoder.hpp
Normal 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__*/
|
30
firmware/application/event.cpp
Normal file
30
firmware/application/event.cpp
Normal 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;
|
||||
}
|
51
firmware/application/event.hpp
Normal file
51
firmware/application/event.hpp
Normal 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__*/
|
273
firmware/application/ffconf.h
Normal file
273
firmware/application/ffconf.h
Normal 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
313
firmware/application/halconf.h
Executable 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_ */
|
||||
|
||||
/** @} */
|
208
firmware/application/irq_controls.cpp
Normal file
208
firmware/application/irq_controls.cpp
Normal 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;
|
||||
}
|
47
firmware/application/irq_controls.hpp
Normal file
47
firmware/application/irq_controls.hpp
Normal 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__*/
|
50
firmware/application/irq_ipc.cpp
Normal file
50
firmware/application/irq_ipc.cpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
27
firmware/application/irq_ipc.hpp
Normal file
27
firmware/application/irq_ipc.hpp
Normal 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__*/
|
65
firmware/application/irq_lcd_frame.cpp
Normal file
65
firmware/application/irq_lcd_frame.cpp
Normal 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();
|
||||
}
|
||||
|
||||
}
|
27
firmware/application/irq_lcd_frame.hpp
Normal file
27
firmware/application/irq_lcd_frame.hpp
Normal 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__*/
|
53
firmware/application/m4_startup.cpp
Normal file
53
firmware/application/m4_startup.cpp
Normal 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);
|
||||
}
|
27
firmware/application/m4_startup.hpp
Normal file
27
firmware/application/m4_startup.hpp
Normal 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
671
firmware/application/main.cpp
Executable 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();
|
||||
}
|
254
firmware/application/max2837.cpp
Normal file
254
firmware/application/max2837.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
902
firmware/application/max2837.hpp
Normal file
902
firmware/application/max2837.hpp
Normal 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__*/
|
36
firmware/application/max5864.cpp
Normal file
36
firmware/application/max5864.cpp
Normal 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());
|
||||
}
|
||||
|
||||
}
|
59
firmware/application/max5864.hpp
Normal file
59
firmware/application/max5864.hpp
Normal 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
69
firmware/application/mcuconf.h
Executable 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
|
172
firmware/application/radio.cpp
Normal file
172
firmware/application/radio.cpp
Normal 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 */
|
52
firmware/application/radio.hpp
Normal file
52
firmware/application/radio.hpp
Normal 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__*/
|
224
firmware/application/receiver_model.cpp
Normal file
224
firmware/application/receiver_model.cpp
Normal 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() );
|
||||
}
|
111
firmware/application/receiver_model.hpp
Normal file
111
firmware/application/receiver_model.hpp
Normal 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__*/
|
224
firmware/application/rf_path.cpp
Normal file
224
firmware/application/rf_path.cpp
Normal 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 */
|
104
firmware/application/rf_path.hpp
Normal file
104
firmware/application/rf_path.hpp
Normal 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__*/
|
296
firmware/application/rffc507x.cpp
Normal file
296
firmware/application/rffc507x.cpp
Normal 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 */
|
835
firmware/application/rffc507x.hpp
Normal file
835
firmware/application/rffc507x.hpp
Normal 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__*/
|
109
firmware/application/rffc507x_spi.cpp
Normal file
109
firmware/application/rffc507x_spi.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
70
firmware/application/rffc507x_spi.hpp
Normal file
70
firmware/application/rffc507x_spi.hpp
Normal 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__*/
|
106
firmware/application/si5351.cpp
Normal file
106
firmware/application/si5351.cpp
Normal 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 */
|
411
firmware/application/si5351.hpp
Normal file
411
firmware/application/si5351.hpp
Normal 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__*/
|
540
firmware/application/spectrum_color_lut.cpp
Normal file
540
firmware/application/spectrum_color_lut.cpp
Normal 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 },
|
||||
} };
|
32
firmware/application/spectrum_color_lut.hpp
Normal file
32
firmware/application/spectrum_color_lut.hpp
Normal 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__*/
|
77
firmware/application/spi_arbiter.hpp
Normal file
77
firmware/application/spi_arbiter.hpp
Normal 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__*/
|
22
firmware/application/spi_pp.cpp
Normal file
22
firmware/application/spi_pp.cpp
Normal 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"
|
56
firmware/application/spi_pp.hpp
Normal file
56
firmware/application/spi_pp.hpp
Normal 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__*/
|
119
firmware/application/touch.cpp
Normal file
119
firmware/application/touch.cpp
Normal 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 */
|
233
firmware/application/touch.hpp
Normal file
233
firmware/application/touch.hpp
Normal 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__*/
|
101
firmware/application/touch_adc.cpp
Normal file
101
firmware/application/touch_adc.cpp
Normal 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 */
|
38
firmware/application/touch_adc.hpp
Normal file
38
firmware/application/touch_adc.hpp
Normal 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__*/
|
100
firmware/application/tuning.cpp
Normal file
100
firmware/application/tuning.cpp
Normal 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 */
|
67
firmware/application/tuning.hpp
Normal file
67
firmware/application/tuning.hpp
Normal 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__*/
|
90
firmware/application/ui_audio.cpp
Normal file
90
firmware/application/ui_audio.cpp
Normal 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 */
|
57
firmware/application/ui_audio.hpp
Normal file
57
firmware/application/ui_audio.hpp
Normal 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__*/
|
79
firmware/application/ui_channel.cpp
Normal file
79
firmware/application/ui_channel.cpp
Normal 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 */
|
55
firmware/application/ui_channel.hpp
Normal file
55
firmware/application/ui_channel.hpp
Normal 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__*/
|
66
firmware/application/ui_console.cpp
Normal file
66
firmware/application/ui_console.cpp
Normal 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 */
|
62
firmware/application/ui_console.hpp
Normal file
62
firmware/application/ui_console.hpp
Normal 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__*/
|
135
firmware/application/ui_debug.cpp
Normal file
135
firmware/application/ui_debug.cpp
Normal 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 */
|
152
firmware/application/ui_debug.hpp
Normal file
152
firmware/application/ui_debug.hpp
Normal 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__*/
|
138
firmware/application/ui_font_fixed_8x16.cpp
Normal file
138
firmware/application/ui_font_fixed_8x16.cpp
Normal 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 */
|
35
firmware/application/ui_font_fixed_8x16.hpp
Normal file
35
firmware/application/ui_font_fixed_8x16.hpp
Normal 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__*/
|
170
firmware/application/ui_menu.cpp
Normal file
170
firmware/application/ui_menu.cpp
Normal 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 */
|
98
firmware/application/ui_menu.hpp
Normal file
98
firmware/application/ui_menu.hpp
Normal 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__*/
|
194
firmware/application/ui_navigation.cpp
Normal file
194
firmware/application/ui_navigation.cpp
Normal 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 */
|
140
firmware/application/ui_navigation.hpp
Normal file
140
firmware/application/ui_navigation.hpp
Normal 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__*/
|
557
firmware/application/ui_receiver.cpp
Normal file
557
firmware/application/ui_receiver.cpp
Normal 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 */
|
443
firmware/application/ui_receiver.hpp
Normal file
443
firmware/application/ui_receiver.hpp
Normal 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__*/
|
106
firmware/application/ui_rssi.cpp
Normal file
106
firmware/application/ui_rssi.cpp
Normal 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 */
|
59
firmware/application/ui_rssi.hpp
Normal file
59
firmware/application/ui_rssi.hpp
Normal 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__*/
|
125
firmware/application/ui_setup.cpp
Normal file
125
firmware/application/ui_setup.cpp
Normal 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 */
|
175
firmware/application/ui_setup.hpp
Normal file
175
firmware/application/ui_setup.hpp
Normal 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__*/
|
26
firmware/application/ui_spectrum.cpp
Normal file
26
firmware/application/ui_spectrum.cpp
Normal 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 */
|
244
firmware/application/ui_spectrum.hpp
Normal file
244
firmware/application/ui_spectrum.hpp
Normal 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
267
firmware/baseband/Makefile
Executable 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
|
44
firmware/baseband/access_code_correlator.cpp
Normal file
44
firmware/baseband/access_code_correlator.cpp
Normal 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);
|
||||
}
|
49
firmware/baseband/access_code_correlator.hpp
Normal file
49
firmware/baseband/access_code_correlator.hpp
Normal 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__*/
|
238
firmware/baseband/audio_dma.cpp
Normal file
238
firmware/baseband/audio_dma.cpp
Normal 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 */
|
43
firmware/baseband/audio_dma.hpp
Normal file
43
firmware/baseband/audio_dma.hpp
Normal 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__*/
|
180
firmware/baseband/baseband_dma.cpp
Normal file
180
firmware/baseband/baseband_dma.cpp
Normal 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 */
|
52
firmware/baseband/baseband_dma.hpp
Normal file
52
firmware/baseband/baseband_dma.hpp
Normal 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__*/
|
90
firmware/baseband/block_decimator.hpp
Normal file
90
firmware/baseband/block_decimator.hpp
Normal 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
546
firmware/baseband/chconf.h
Executable 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_ */
|
||||
|
||||
/** @} */
|
31
firmware/baseband/clock_recovery.cpp
Normal file
31
firmware/baseband/clock_recovery.cpp
Normal 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)
|
||||
);
|
||||
}
|
86
firmware/baseband/clock_recovery.hpp
Normal file
86
firmware/baseband/clock_recovery.hpp
Normal 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__*/
|
460
firmware/baseband/dsp_decimate.cpp
Normal file
460
firmware/baseband/dsp_decimate.cpp
Normal 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 */
|
242
firmware/baseband/dsp_decimate.hpp
Normal file
242
firmware/baseband/dsp_decimate.hpp
Normal 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__*/
|
110
firmware/baseband/dsp_demodulate.cpp
Normal file
110
firmware/baseband/dsp_demodulate.cpp
Normal 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 };
|
||||
}
|
||||
|
||||
}
|
||||
}
|
66
firmware/baseband/dsp_demodulate.hpp
Normal file
66
firmware/baseband/dsp_demodulate.hpp
Normal 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__*/
|
22
firmware/baseband/dsp_fir_taps.cpp
Normal file
22
firmware/baseband/dsp_fir_taps.cpp
Normal 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"
|
285
firmware/baseband/dsp_fir_taps.hpp
Normal file
285
firmware/baseband/dsp_fir_taps.hpp
Normal 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__*/
|
68
firmware/baseband/dsp_iir.hpp
Normal file
68
firmware/baseband/dsp_iir.hpp
Normal 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__*/
|
30
firmware/baseband/event_m4.cpp
Normal file
30
firmware/baseband/event_m4.cpp
Normal 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;
|
||||
}
|
46
firmware/baseband/event_m4.hpp
Normal file
46
firmware/baseband/event_m4.hpp
Normal 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__*/
|
152
firmware/baseband/fxpt_atan2.cpp
Normal file
152
firmware/baseband/fxpt_atan2.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
29
firmware/baseband/fxpt_atan2.hpp
Normal file
29
firmware/baseband/fxpt_atan2.hpp
Normal 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
Loading…
Reference in New Issue
Block a user