removed erronousely copied directory
|
@ -1 +0,0 @@
|
|||
build
|
|
@ -1,155 +0,0 @@
|
|||
cmake_minimum_required(VERSION 3.15)
|
||||
project(cariboulite
|
||||
VERSION 1.2.0
|
||||
LANGUAGES C CXX
|
||||
DESCRIPTION "CaribouLite RpiSDR C API")
|
||||
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
# Bring the headers
|
||||
include_directories(./src)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/src)
|
||||
add_compile_options(-Wall -Wextra -Wno-unused-variable -Wno-missing-braces)
|
||||
|
||||
# ------------------------------------
|
||||
# MAIN - Source files for main library
|
||||
# ------------------------------------
|
||||
set(SOURCES_LIB src/cariboulite.c src/cariboulite_setup.c src/cariboulite_events.c src/cariboulite_radio.c)
|
||||
set(TARGET_LINK_LIBS datatypes
|
||||
production_utils
|
||||
caribou_fpga
|
||||
at86rf215
|
||||
rffc507x
|
||||
caribou_smi
|
||||
caribou_prog
|
||||
hat
|
||||
io_utils
|
||||
zf_log
|
||||
rt
|
||||
m
|
||||
pthread)
|
||||
|
||||
set(SOURCES_CPP_LIB src/CaribouLiteCpp.cpp src/CaribouLiteRadioCpp.cpp)
|
||||
|
||||
# Add internal project dependencies
|
||||
add_subdirectory(src/datatypes EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(src/caribou_programming EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(src/caribou_fpga EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(src/at86rf215 EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(src/caribou_smi EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(src/io_utils EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(src/rffc507x EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(src/hat EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(src/production_utils EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(src/zf_log EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(src/iir EXCLUDE_FROM_ALL)
|
||||
|
||||
# Create the library cariboulite
|
||||
add_library(cariboulite STATIC ${SOURCES_LIB} ${SOURCES_CPP_LIB})
|
||||
target_link_libraries(cariboulite PRIVATE ${TARGET_LINK_LIBS})
|
||||
set_target_properties(cariboulite PROPERTIES PUBLIC_HEADER "src/cariboulite.h;src/cariboulite_radio.h;src/CaribouLite.hpp")
|
||||
set_target_properties(cariboulite PROPERTIES OUTPUT_NAME cariboulite)
|
||||
|
||||
add_library(cariboulite_shared SHARED ${SOURCES_LIB} ${SOURCES_CPP_LIB})
|
||||
target_link_libraries(cariboulite_shared PRIVATE ${TARGET_LINK_LIBS})
|
||||
set_target_properties(cariboulite_shared PROPERTIES PUBLIC_HEADER "src/cariboulite.h;src/cariboulite_radio.h;src/CaribouLite.hpp")
|
||||
set_property(TARGET cariboulite_shared PROPERTY POSITION_INDEPENDENT_CODE 1)
|
||||
set_target_properties(cariboulite_shared PROPERTIES OUTPUT_NAME cariboulite)
|
||||
|
||||
|
||||
|
||||
#--------------------------------------
|
||||
# create the Soapy shared object
|
||||
#--------------------------------------
|
||||
find_package(SoapySDR CONFIG)
|
||||
if (NOT SoapySDR_FOUND)
|
||||
message(WARNING "SoapySDR development files not found - skipping support")
|
||||
return()
|
||||
endif ()
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
include(CheckCXXCompilerFlag)
|
||||
CHECK_CXX_COMPILER_FLAG("-std=c++11" HAS_STD_CXX11)
|
||||
if(HAS_STD_CXX11)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -O3")
|
||||
else(HAS_STD_CXX11)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -O3")
|
||||
endif()
|
||||
#Thread support enabled (not the same as -lpthread)
|
||||
list(APPEND SOAPY_CARIBOULITE_LIBRARIES)
|
||||
add_definitions(-Wno-unused-parameter -Wno-missing-field-initializers -Wno-parentheses -Wno-psabi)
|
||||
endif(CMAKE_COMPILER_IS_GNUCXX)
|
||||
|
||||
execute_process (
|
||||
COMMAND bash -c "SoapySDRUtil --info | grep 'Search path' | cut -d':' -f2 | xargs | cut -d' ' -f1 | xargs"
|
||||
OUTPUT_VARIABLE SOAPY_DEST
|
||||
)
|
||||
string(STRIP ${SOAPY_DEST} SOAPY_DEST)
|
||||
|
||||
execute_process (
|
||||
COMMAND bash -c "SoapySDRUtil --info | grep 'Search path' | cut -d':' -f2 | xargs | cut -d' ' -f1 | awk -F '/lib/' '{print $1}' | xargs"
|
||||
OUTPUT_VARIABLE BIN_DEST
|
||||
)
|
||||
string(STRIP ${BIN_DEST} BIN_DEST)
|
||||
|
||||
SOAPY_SDR_MODULE_UTIL(
|
||||
TARGET SoapyCariboulite
|
||||
SOURCES
|
||||
src/soapy_api/SoapyCariboulite.cpp
|
||||
src/soapy_api/Cariboulite.cpp
|
||||
src/soapy_api/Cariboulite.hpp
|
||||
src/soapy_api/CaribouliteStreamFunctions.cpp
|
||||
src/soapy_api/CaribouliteStream.cpp
|
||||
src/soapy_api/CaribouliteSession.cpp
|
||||
src/soapy_api/CaribouliteSensors.cpp
|
||||
LIBRARIES cariboulite iir
|
||||
DESTINATION ${SOAPY_DEST}
|
||||
PREFIX ""
|
||||
)
|
||||
|
||||
# ----------------------------------
|
||||
# TESTS - source files for the tests
|
||||
# ----------------------------------
|
||||
set(SOURCES_CARIBOU_PROGRAMMER test/caribou_programmer.c)
|
||||
set(SOURCES_FPGA_COMM test/fpga_comm_test.c)
|
||||
set(SOURCES_TEST_MAIN src/cariboulite_test_app.c src/app_menu.c)
|
||||
set(SOURCES_MAIN src/cariboulite_util.c)
|
||||
set(SOURCES_PROD src/cariboulite_production.c)
|
||||
|
||||
add_executable(caribou_programmer ${SOURCES_CARIBOU_PROGRAMMER})
|
||||
add_executable(fpgacomm ${SOURCES_FPGA_COMM})
|
||||
add_executable(cariboulite_test_app ${SOURCES_TEST_MAIN})
|
||||
add_executable(cariboulite_util ${SOURCES_MAIN})
|
||||
|
||||
target_link_libraries(caribou_programmer cariboulite)
|
||||
target_link_libraries(fpgacomm cariboulite)
|
||||
target_link_libraries(cariboulite_test_app cariboulite)
|
||||
target_link_libraries(cariboulite_util cariboulite)
|
||||
|
||||
set_target_properties( caribou_programmer PROPERTIES RUNTIME_OUTPUT_DIRECTORY test)
|
||||
set_target_properties( fpgacomm PROPERTIES RUNTIME_OUTPUT_DIRECTORY test)
|
||||
|
||||
# ------------
|
||||
# INSTALLATION
|
||||
# ------------
|
||||
|
||||
install(TARGETS cariboulite
|
||||
LIBRARY DESTINATION ${BIN_DEST}/lib/
|
||||
ARCHIVE DESTINATION ${BIN_DEST}/lib/
|
||||
PUBLIC_HEADER DESTINATION ${BIN_DEST}/include/)
|
||||
|
||||
install(TARGETS cariboulite_shared
|
||||
LIBRARY DESTINATION ${BIN_DEST}/lib/
|
||||
ARCHIVE DESTINATION ${BIN_DEST}/lib/
|
||||
PUBLIC_HEADER DESTINATION ${BIN_DEST}/include/)
|
||||
|
||||
# generate pc file for pkg-config
|
||||
set(PC_FILE cariboulite)
|
||||
configure_file(cariboulite.pc.in
|
||||
cariboulite.pc @ONLY)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/cariboulite.pc
|
||||
DESTINATION ${BIN_DEST}/lib/pkgconfig/)
|
||||
|
||||
#install(TARGETS cariboulite_test_app DESTINATION ${BIN_DEST}/bin/)
|
||||
install(TARGETS cariboulite_util DESTINATION ${BIN_DEST}/bin/)
|
|
@ -1,46 +0,0 @@
|
|||
# CaribouLite RPI API
|
||||
This folder contains the low-level and SoapySDR APIs for CaribouLite
|
||||
|
||||
# Building
|
||||
To start from scratch we need to check for the dependencies and install them as needed.
|
||||
|
||||
Dependencies installation:
|
||||
```
|
||||
# Update the package definitions
|
||||
sudo apt update
|
||||
|
||||
# We used gcc version 8.3+
|
||||
sudo apt install gcc
|
||||
|
||||
# We used libsoapysdr-dev version 0.6.1-4+
|
||||
sudo apt install libsoapysdr-dev libpthread-stubs0-dev
|
||||
|
||||
# cmake version 3.15+
|
||||
sudo apt install cmake
|
||||
```
|
||||
|
||||
Now to compile we use `cmake` as follows:
|
||||
|
||||
```
|
||||
# create the building directory to contain
|
||||
# compilation / linking artifacts. if already exists skip
|
||||
# the creation
|
||||
mkdir build
|
||||
|
||||
# goto the build directory
|
||||
cd build
|
||||
|
||||
# tell cmake to create the Makefiles
|
||||
# according to the code in the parent directory
|
||||
cmake ../
|
||||
|
||||
# build the code (this will take about 30 seconds @ RPi4)
|
||||
make
|
||||
|
||||
# install the package in your Linux
|
||||
# environment (including SoapyAPIs)
|
||||
sudo make install
|
||||
```
|
||||
|
||||
# License
|
||||
<a rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by/4.0/88x31.png" /></a><br />This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>.
|
|
@ -1,11 +0,0 @@
|
|||
prefix="@BIN_DEST@"
|
||||
exec_prefix="${prefix}"/bin
|
||||
libdir="${prefix}/lib"
|
||||
includedir="${prefix}/include"
|
||||
|
||||
Name: @PROJECT_NAME@
|
||||
Description: @CMAKE_PROJECT_DESCRIPTION@
|
||||
Version: @PROJECT_VERSION@
|
||||
Cflags: -I${includedir}
|
||||
Libs: -L${libdir} -l@PROJECT_NAME@
|
||||
|
|
@ -1,261 +0,0 @@
|
|||
/**
|
||||
* @file CaribouLite.hpp
|
||||
* @author David Michaeli
|
||||
* @date September 2023
|
||||
* @brief Main Init/Close API
|
||||
*
|
||||
* A high level CPP API for CaribouLite
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __CARIBOULITE_HPP__
|
||||
#define __CARIBOULITE_HPP__
|
||||
|
||||
#include <cariboulite.h>
|
||||
#include <cariboulite_radio.h>
|
||||
|
||||
#include <vector>
|
||||
#include <complex>
|
||||
#include <cstddef>
|
||||
#include <ostream>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <functional>
|
||||
|
||||
#if __cplusplus <= 199711L
|
||||
#error This file needs at least a C++11 compliant compiler, try using:
|
||||
#error $ g++ -std=c++11 ..
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief CaribouLite API Version
|
||||
*/
|
||||
class CaribouLiteVersion
|
||||
{
|
||||
public:
|
||||
CaribouLiteVersion(int v, int sv, int rev) {_ver.major_version = v; _ver.minor_version = sv; _ver.revision = rev;}
|
||||
virtual ~CaribouLiteVersion() {}
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const CaribouLiteVersion& c)
|
||||
{
|
||||
os << c._ver.major_version << '.' << c._ver.minor_version << '/' << c._ver.revision;
|
||||
return os;
|
||||
}
|
||||
private:
|
||||
cariboulite_lib_version_st _ver;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief CaribouLite Frequency Range
|
||||
*/
|
||||
class CaribouLiteFreqRange
|
||||
{
|
||||
public:
|
||||
CaribouLiteFreqRange(float min_hz, float max_hz) : _fMin(min_hz), _fMax(max_hz) {}
|
||||
virtual ~CaribouLiteFreqRange() {}
|
||||
friend std::ostream& operator<<(std::ostream& os, const CaribouLiteFreqRange& r)
|
||||
{
|
||||
os << "[" << r._fMin << ',' << r._fMax << ']';
|
||||
return os;
|
||||
}
|
||||
private:
|
||||
float _fMin, _fMax;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @brief CaribouLite Radio Channel
|
||||
*/
|
||||
#pragma pack(1)
|
||||
struct CaribouLiteComplexInt
|
||||
{
|
||||
int16_t i;
|
||||
int16_t q;
|
||||
};
|
||||
|
||||
struct CaribouLiteMeta
|
||||
{
|
||||
uint8_t sync;
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
class CaribouLite;
|
||||
class CaribouLiteRadio
|
||||
{
|
||||
public:
|
||||
enum RadioType
|
||||
{
|
||||
S1G = 0,
|
||||
HiF = 1,
|
||||
};
|
||||
|
||||
enum RadioDir
|
||||
{
|
||||
Rx = 0,
|
||||
Tx = 1,
|
||||
};
|
||||
|
||||
enum RxCbType
|
||||
{
|
||||
None = 0,
|
||||
FloatSync = 1,
|
||||
Float = 2,
|
||||
IntSync = 3,
|
||||
Int = 4,
|
||||
};
|
||||
|
||||
public:
|
||||
CaribouLiteRadio(const cariboulite_radio_state_st* radio, RadioType type, const CaribouLite* parent = NULL);
|
||||
virtual ~CaribouLiteRadio();
|
||||
|
||||
// Gain
|
||||
void SetAgc(bool agc_on);
|
||||
bool GetAgc(void);
|
||||
float GetRxGainMin(void);
|
||||
float GetRxGainMax(void);
|
||||
float GetRxGainSteps(void);
|
||||
void SetRxGain(float gain);
|
||||
float GetRxGain(void);
|
||||
|
||||
// Tx Power
|
||||
float GetTxPowerMin(void);
|
||||
float GetTxPowerMax(void);
|
||||
float GetTxPowerStep(void);
|
||||
void SetTxPower(float pwr_dBm);
|
||||
float GetTxPower(void);
|
||||
|
||||
// Rx Bandwidth
|
||||
float GetRxBandwidthMin(void);
|
||||
float GetRxBandwidthMax(void);
|
||||
void SetRxBandwidth(float bw_hz);
|
||||
float GetRxBandwidth(void);
|
||||
|
||||
// Tx Bandwidth
|
||||
float GetTxBandwidthMin(void);
|
||||
float GetTxBandwidthMax(void);
|
||||
void SetTxBandwidth(float bw_hz);
|
||||
float GetTxBandwidth(void);
|
||||
|
||||
// Rx Sample Rate
|
||||
float GetRxSampleRateMin(void);
|
||||
float GetRxSampleRateMax(void);
|
||||
void SetRxSampleRate(float sr_hz);
|
||||
float GetRxSampleRate(void);
|
||||
|
||||
// Tx Sample Rate
|
||||
float GetTxSampleRateMin(void);
|
||||
float GetTxSampleRateMax(void);
|
||||
void SetTxSampleRate(float sr_hz);
|
||||
float GetTxSampleRate(void);
|
||||
|
||||
// RSSI and Rx Power and others
|
||||
float GetRssi(void);
|
||||
float GetEnergyDet(void);
|
||||
unsigned char GetTrueRandVal(void);
|
||||
|
||||
// Frequency Control
|
||||
void SetFrequency(float freq_hz);
|
||||
float GetFrequency(void);
|
||||
std::vector<CaribouLiteFreqRange> GetFrequencyRange(void);
|
||||
float GetFrequencyResolution(void);
|
||||
|
||||
// Activation
|
||||
void StartReceiving(std::function<void(CaribouLiteRadio*, const std::complex<float>*, CaribouLiteMeta*, size_t)> on_data_ready, size_t samples_per_chunk = 0);
|
||||
void StartReceiving(std::function<void(CaribouLiteRadio*, const std::complex<float>*, size_t)> on_data_ready, size_t samples_per_chunk = 0);
|
||||
void StartReceiving(std::function<void(CaribouLiteRadio*, const CaribouLiteComplexInt*, CaribouLiteMeta*, size_t)> on_data_ready, size_t samples_per_chunk = 0);
|
||||
void StartReceiving(std::function<void(CaribouLiteRadio*, const CaribouLiteComplexInt*, size_t)> on_data_ready, size_t samples_per_chunk = 0);
|
||||
void StartReceivingInternal(size_t samples_per_chunk);
|
||||
void StopReceiving(void);
|
||||
void StartTransmitting(std::function<void(CaribouLiteRadio*, std::complex<float>*, const bool*, size_t*)> on_data_request, size_t samples_per_chunk);
|
||||
void StartTransmittingLo(void);
|
||||
void StartTransmittingCw(void);
|
||||
void StopTransmitting(void);
|
||||
bool GetIsTransmittingLo(void);
|
||||
bool GetIsTransmittingCw(void);
|
||||
|
||||
// General
|
||||
size_t GetNativeMtuSample(void);
|
||||
std::string GetRadioName(void);
|
||||
void FlushBuffers(void);
|
||||
|
||||
private:
|
||||
const cariboulite_radio_state_st* _radio;
|
||||
const CaribouLite* _device;
|
||||
const RadioType _type;
|
||||
|
||||
bool _rx_thread_running;
|
||||
bool _rx_is_active;
|
||||
std::thread *_rx_thread;
|
||||
std::function<void(CaribouLiteRadio*, const std::complex<float>*, CaribouLiteMeta*, size_t)> _on_data_ready_fm;
|
||||
std::function<void(CaribouLiteRadio*, const std::complex<float>*, size_t)> _on_data_ready_f;
|
||||
std::function<void(CaribouLiteRadio*, const CaribouLiteComplexInt*, CaribouLiteMeta*, size_t)> _on_data_ready_im;
|
||||
std::function<void(CaribouLiteRadio*, const CaribouLiteComplexInt*, size_t)> _on_data_ready_i;
|
||||
size_t _rx_samples_per_chunk;
|
||||
RxCbType _rxCallbackType;
|
||||
|
||||
bool _tx_thread_running;
|
||||
bool _tx_is_active;
|
||||
std::thread *_tx_thread;
|
||||
std::function<void(CaribouLiteRadio*, std::complex<float>*, const bool*, size_t*)> _on_data_request;
|
||||
size_t _tx_samples_per_chunk;
|
||||
|
||||
private:
|
||||
static void CaribouLiteRxThread(CaribouLiteRadio* radio);
|
||||
static void CaribouLiteTxThread(CaribouLiteRadio* radio);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief CaribouLite Device
|
||||
*/
|
||||
class CaribouLite
|
||||
{
|
||||
public:
|
||||
enum LogLevel
|
||||
{
|
||||
Verbose,
|
||||
Info,
|
||||
None,
|
||||
};
|
||||
|
||||
enum SysVersion
|
||||
{
|
||||
Unknown = 0,
|
||||
CaribouLiteFull = 1,
|
||||
CaribouLiteISM = 2,
|
||||
};
|
||||
|
||||
protected:
|
||||
CaribouLite(bool forceFpgaProg = false, LogLevel logLvl = LogLevel::None);
|
||||
CaribouLite(const CaribouLite& o) = delete;
|
||||
void operator=(const CaribouLite&) = delete;
|
||||
void ReleaseResources(void);
|
||||
|
||||
public:
|
||||
~CaribouLite();
|
||||
bool IsInitialized(void);
|
||||
CaribouLiteVersion GetApiVersion(void);
|
||||
unsigned int GetHwSerialNumber(void);
|
||||
SysVersion GetSystemVersion(void);
|
||||
std::string GetSystemVersionStr(void);
|
||||
static std::string GetSystemVersionStr(SysVersion v);
|
||||
std::string GetHwGuid(void);
|
||||
CaribouLiteRadio* GetRadioChannel(CaribouLiteRadio::RadioType ch);
|
||||
|
||||
// Ststic detection and factory
|
||||
static CaribouLite &GetInstance(bool forceFpgaProg = false, LogLevel logLvl = LogLevel::None);
|
||||
static bool DetectBoard(SysVersion *sysVer, std::string& name, std::string& guid);
|
||||
static void DefaultSignalHandler(void* context, int signal_number, siginfo_t *si);
|
||||
|
||||
private:
|
||||
std::vector<CaribouLiteRadio*> _channels;
|
||||
SysVersion _systemVersion;
|
||||
std::string _productName;
|
||||
std::string _productGuid;
|
||||
|
||||
static std::shared_ptr<CaribouLite> _instance;
|
||||
static std::mutex _instMutex;
|
||||
};
|
||||
|
||||
#endif // __CARIBOULITE_HPP__
|
|
@ -1,160 +0,0 @@
|
|||
#include <exception>
|
||||
#include "CaribouLite.hpp"
|
||||
|
||||
std::shared_ptr<CaribouLite> CaribouLite::_instance = nullptr;
|
||||
std::mutex CaribouLite::_instMutex;
|
||||
|
||||
//==================================================================
|
||||
void CaribouLite::DefaultSignalHandler(void* context, int signal_number, siginfo_t *si)
|
||||
{
|
||||
CaribouLite* cl = (CaribouLite*)context;
|
||||
std::cout << " >> Signal caught: " << signal_number << std::endl << std::flush;
|
||||
cl->ReleaseResources();
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
bool CaribouLite::DetectBoard(SysVersion *sysVer, std::string& name, std::string& guid)
|
||||
{
|
||||
cariboulite_version_en hw_ver;
|
||||
char hw_name[64];
|
||||
char hw_guid[64];
|
||||
bool detected = cariboulite_detect_connected_board(&hw_ver, hw_name, hw_guid);
|
||||
if (detected)
|
||||
{
|
||||
name = std::string(hw_name);
|
||||
guid = std::string(hw_guid);
|
||||
if (sysVer) *sysVer = (SysVersion)hw_ver;
|
||||
}
|
||||
return detected;
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
CaribouLite &CaribouLite::GetInstance(bool forceFpgaProg, LogLevel logLvl)
|
||||
{
|
||||
SysVersion ver;
|
||||
std::string name, guid;
|
||||
if (!DetectBoard(&ver, name, guid))
|
||||
{
|
||||
throw std::runtime_error("CaribouLite was not detected");
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(_instMutex);
|
||||
if (_instance == nullptr)
|
||||
{
|
||||
try
|
||||
{
|
||||
_instance = std::shared_ptr<CaribouLite>(new CaribouLite(forceFpgaProg, logLvl));
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return *_instance;
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
CaribouLite::CaribouLite(bool forceFpgaProg, LogLevel logLvl)
|
||||
{
|
||||
if (cariboulite_init(forceFpgaProg, (cariboulite_log_level_en)logLvl) != 0)
|
||||
{
|
||||
throw std::runtime_error("Driver initialization failed");
|
||||
}
|
||||
|
||||
// register signal handler
|
||||
cariboulite_register_signal_handler (CaribouLite::DefaultSignalHandler, this);
|
||||
|
||||
// get information
|
||||
DetectBoard(&_systemVersion, _productName, _productGuid);
|
||||
|
||||
// populate the radio devices
|
||||
cariboulite_radio_state_st *radio_s1g = cariboulite_get_radio(cariboulite_channel_s1g);
|
||||
CaribouLiteRadio* radio_s1g_int = new CaribouLiteRadio(radio_s1g, CaribouLiteRadio::RadioType::S1G, this);
|
||||
_channels.push_back(radio_s1g_int);
|
||||
|
||||
cariboulite_radio_state_st *radio_hif = cariboulite_get_radio(cariboulite_channel_hif);
|
||||
CaribouLiteRadio* radio_hif_int = new CaribouLiteRadio(radio_hif, CaribouLiteRadio::RadioType::HiF, this);
|
||||
_channels.push_back(radio_hif_int);
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
void CaribouLite::ReleaseResources(void)
|
||||
{
|
||||
if (!_instance) return;
|
||||
for (size_t i = 0; i < _instance->_channels.size(); i++)
|
||||
{
|
||||
if (_instance->_channels[i]) delete _instance->_channels[i];
|
||||
_instance->_channels[i] = NULL;
|
||||
}
|
||||
if (cariboulite_is_initialized()) cariboulite_close();
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
CaribouLite::~CaribouLite()
|
||||
{
|
||||
if (_instance != nullptr)
|
||||
{
|
||||
ReleaseResources();
|
||||
|
||||
_instance.reset();
|
||||
_instance = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
bool CaribouLite::IsInitialized()
|
||||
{
|
||||
return cariboulite_is_initialized();
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
CaribouLiteVersion CaribouLite::GetApiVersion()
|
||||
{
|
||||
cariboulite_lib_version_st v = {0};
|
||||
cariboulite_get_lib_version(&v);
|
||||
return CaribouLiteVersion(v.major_version, v.minor_version, v.revision);
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
unsigned int CaribouLite::GetHwSerialNumber()
|
||||
{
|
||||
return cariboulite_get_sn();
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
CaribouLite::SysVersion CaribouLite::GetSystemVersion()
|
||||
{
|
||||
return (CaribouLite::SysVersion)cariboulite_get_version();
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
std::string CaribouLite::GetSystemVersionStr(CaribouLite::SysVersion v)
|
||||
{
|
||||
switch(v)
|
||||
{
|
||||
case SysVersion::CaribouLiteFull: return std::string("CaribouLite6G"); break;
|
||||
case SysVersion::CaribouLiteISM: return std::string("CaribouLiteISM"); break;
|
||||
case SysVersion::Unknown:
|
||||
default: return std::string("Unknown CaribouLite"); break;
|
||||
}
|
||||
return std::string(""); // unreachable.. hopefully
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
std::string CaribouLite::GetSystemVersionStr(void)
|
||||
{
|
||||
return CaribouLite::GetSystemVersionStr(GetSystemVersion());
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
std::string CaribouLite::GetHwGuid(void)
|
||||
{
|
||||
return _productGuid;
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
CaribouLiteRadio* CaribouLite::GetRadioChannel(CaribouLiteRadio::RadioType ch)
|
||||
{
|
||||
return _channels[(int)ch];
|
||||
}
|
||||
|
|
@ -1,542 +0,0 @@
|
|||
#include "CaribouLite.hpp"
|
||||
|
||||
//=================================================================
|
||||
void CaribouLiteRadio::CaribouLiteRxThread(CaribouLiteRadio* radio)
|
||||
{
|
||||
size_t mtu_size = radio->GetNativeMtuSample();
|
||||
CaribouLiteComplexInt* rx_buffer = new CaribouLiteComplexInt[mtu_size];
|
||||
CaribouLiteMeta* rx_meta_buffer = new CaribouLiteMeta[mtu_size];
|
||||
std::complex<float>* rx_copmlex_data = new std::complex<float>[mtu_size];
|
||||
|
||||
while (radio->_rx_thread_running)
|
||||
{
|
||||
if (!radio->_rx_is_active)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(2));
|
||||
continue;
|
||||
}
|
||||
|
||||
int ret = cariboulite_radio_read_samples((cariboulite_radio_state_st*)radio->_radio,
|
||||
(cariboulite_sample_complex_int16*)rx_buffer,
|
||||
(cariboulite_sample_meta*)rx_meta_buffer,
|
||||
radio->_rx_samples_per_chunk);
|
||||
if (ret < 0)
|
||||
{
|
||||
if (ret == -1)
|
||||
{
|
||||
//printf("reader thread failed to read SMI!\n");
|
||||
}
|
||||
ret = 0;
|
||||
continue;
|
||||
}
|
||||
if (ret == 0) continue;
|
||||
|
||||
// convert the buffer
|
||||
if (radio->_rxCallbackType == CaribouLiteRadio::RxCbType::FloatSync || radio->_rxCallbackType == CaribouLiteRadio::RxCbType::Float)
|
||||
{
|
||||
for (int i = 0; i < ret; i ++)
|
||||
{
|
||||
rx_copmlex_data[i].real(rx_buffer[i].i / 4096.0);
|
||||
rx_copmlex_data[i].imag(rx_buffer[i].q / 4096.0);
|
||||
}
|
||||
}
|
||||
|
||||
// notify application
|
||||
try
|
||||
{
|
||||
switch(radio->_rxCallbackType)
|
||||
{
|
||||
case (CaribouLiteRadio::RxCbType::FloatSync): if (radio->_on_data_ready_fm) radio->_on_data_ready_fm(radio, rx_copmlex_data, rx_meta_buffer, ret); break;
|
||||
case (CaribouLiteRadio::RxCbType::Float): if (radio->_on_data_ready_f) radio->_on_data_ready_f(radio, rx_copmlex_data, ret); break;
|
||||
case (CaribouLiteRadio::RxCbType::IntSync): if (radio->_on_data_ready_im) radio->_on_data_ready_im(radio, rx_buffer, rx_meta_buffer, ret); break;
|
||||
case (CaribouLiteRadio::RxCbType::Int): if (radio->_on_data_ready_i) radio->_on_data_ready_i(radio, rx_buffer, ret); break;
|
||||
case (CaribouLiteRadio::RxCbType::None):
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
catch (std::exception &e)
|
||||
{
|
||||
std::cout << "OnDataReady Exception: " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
delete[]rx_buffer;
|
||||
delete[]rx_meta_buffer;
|
||||
delete[]rx_copmlex_data;
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
void CaribouLiteRadio::CaribouLiteTxThread(CaribouLiteRadio* radio)
|
||||
{
|
||||
while (radio->_tx_thread_running)
|
||||
{
|
||||
if (!radio->_tx_is_active)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(4));
|
||||
continue;
|
||||
}
|
||||
// TBD
|
||||
}
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
CaribouLiteRadio::CaribouLiteRadio(const cariboulite_radio_state_st* radio, RadioType type, const CaribouLite* parent)
|
||||
: _radio(radio), _device(parent), _type(type), _rxCallbackType(RxCbType::None)
|
||||
{
|
||||
_rx_thread_running = true;
|
||||
_rx_thread = new std::thread(CaribouLiteRadio::CaribouLiteRxThread, this);
|
||||
|
||||
_tx_thread_running = true;
|
||||
_tx_thread = new std::thread(CaribouLiteRadio::CaribouLiteTxThread, this);
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
CaribouLiteRadio::~CaribouLiteRadio()
|
||||
{
|
||||
//std::cout << "Destructor of Radio" << std::endl;
|
||||
StopReceiving();
|
||||
StopTransmitting();
|
||||
|
||||
_rx_thread_running = false;
|
||||
_rx_thread->join();
|
||||
if (_rx_thread) delete _rx_thread;
|
||||
|
||||
_tx_thread_running = false;
|
||||
_tx_thread->join();
|
||||
if (_tx_thread) delete _tx_thread;
|
||||
}
|
||||
|
||||
// Gain
|
||||
//==================================================================
|
||||
void CaribouLiteRadio::SetAgc(bool agc_on)
|
||||
{
|
||||
int gain = 0;
|
||||
bool temp = false;
|
||||
cariboulite_radio_get_rx_gain_control((cariboulite_radio_state_st*)_radio, &temp, &gain);
|
||||
cariboulite_radio_set_rx_gain_control((cariboulite_radio_state_st*)_radio, agc_on, gain);
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
bool CaribouLiteRadio::GetAgc()
|
||||
{
|
||||
int gain = 0;
|
||||
bool temp = false;
|
||||
cariboulite_radio_get_rx_gain_control((cariboulite_radio_state_st*)_radio, &temp, &gain);
|
||||
return temp;
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
float CaribouLiteRadio::GetRxGainMin()
|
||||
{
|
||||
int rx_min_gain_value_db, rx_max_gain_value_db, rx_gain_value_resolution_db;
|
||||
cariboulite_radio_get_rx_gain_limits((cariboulite_radio_state_st*)_radio,
|
||||
&rx_min_gain_value_db,
|
||||
&rx_max_gain_value_db,
|
||||
&rx_gain_value_resolution_db);
|
||||
|
||||
return rx_min_gain_value_db;
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
float CaribouLiteRadio::GetRxGainMax()
|
||||
{
|
||||
int rx_min_gain_value_db, rx_max_gain_value_db, rx_gain_value_resolution_db;
|
||||
cariboulite_radio_get_rx_gain_limits((cariboulite_radio_state_st*)_radio,
|
||||
&rx_min_gain_value_db,
|
||||
&rx_max_gain_value_db,
|
||||
&rx_gain_value_resolution_db);
|
||||
|
||||
return rx_max_gain_value_db;
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
float CaribouLiteRadio::GetRxGainSteps()
|
||||
{
|
||||
int rx_min_gain_value_db, rx_max_gain_value_db, rx_gain_value_resolution_db;
|
||||
cariboulite_radio_get_rx_gain_limits((cariboulite_radio_state_st*)_radio,
|
||||
&rx_min_gain_value_db,
|
||||
&rx_max_gain_value_db,
|
||||
&rx_gain_value_resolution_db);
|
||||
|
||||
return rx_gain_value_resolution_db;
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
void CaribouLiteRadio::SetRxGain(float gain)
|
||||
{
|
||||
bool temp = false;
|
||||
cariboulite_radio_get_rx_gain_control((cariboulite_radio_state_st*)_radio, &temp, NULL);
|
||||
cariboulite_radio_set_rx_gain_control((cariboulite_radio_state_st*)_radio, temp, (int)gain);
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
float CaribouLiteRadio::GetRxGain()
|
||||
{
|
||||
int igain = 0;
|
||||
cariboulite_radio_get_rx_gain_control((cariboulite_radio_state_st*)_radio, NULL, &igain);
|
||||
return (float)igain;
|
||||
}
|
||||
|
||||
// Tx Power
|
||||
|
||||
//==================================================================
|
||||
float CaribouLiteRadio::GetTxPowerMin()
|
||||
{
|
||||
return -15.0f;
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
float CaribouLiteRadio::GetTxPowerMax()
|
||||
{
|
||||
return 18.0f;
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
float CaribouLiteRadio::GetTxPowerStep()
|
||||
{
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
void CaribouLiteRadio::SetTxPower(float pwr_dBm)
|
||||
{
|
||||
cariboulite_radio_set_tx_power((cariboulite_radio_state_st*)_radio, (int)pwr_dBm);
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
float CaribouLiteRadio::GetTxPower()
|
||||
{
|
||||
int itx_pwr = 0;
|
||||
cariboulite_radio_get_tx_power((cariboulite_radio_state_st*)_radio, &itx_pwr);
|
||||
return (float)itx_pwr;
|
||||
}
|
||||
|
||||
// Rx Bandwidth
|
||||
|
||||
//==================================================================
|
||||
float CaribouLiteRadio::GetRxBandwidthMin()
|
||||
{
|
||||
return 200000.0f;
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
float CaribouLiteRadio::GetRxBandwidthMax()
|
||||
{
|
||||
return 2500000.0f;
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
void CaribouLiteRadio::SetRxBandwidth(float bw_hz)
|
||||
{
|
||||
cariboulite_radio_set_rx_bandwidth_flt((cariboulite_radio_state_st*)_radio, bw_hz);
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
float CaribouLiteRadio::GetRxBandwidth()
|
||||
{
|
||||
float bw = 0;
|
||||
cariboulite_radio_get_rx_bandwidth_flt((cariboulite_radio_state_st*)_radio, &bw);
|
||||
return bw;
|
||||
}
|
||||
|
||||
// Tx Bandwidth
|
||||
|
||||
//==================================================================
|
||||
float CaribouLiteRadio::GetTxBandwidthMin()
|
||||
{
|
||||
return 80000.0f;
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
float CaribouLiteRadio::GetTxBandwidthMax()
|
||||
{
|
||||
return 1000000.0f;
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
void CaribouLiteRadio::SetTxBandwidth(float bw_hz)
|
||||
{
|
||||
cariboulite_radio_set_tx_bandwidth_flt((cariboulite_radio_state_st*)_radio, bw_hz);
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
float CaribouLiteRadio::GetTxBandwidth()
|
||||
{
|
||||
float bw = 0;
|
||||
cariboulite_radio_get_tx_bandwidth_flt((cariboulite_radio_state_st*)_radio, &bw);
|
||||
return bw;
|
||||
}
|
||||
|
||||
// Rx Sample Rate
|
||||
|
||||
//==================================================================
|
||||
float CaribouLiteRadio::GetRxSampleRateMin()
|
||||
{
|
||||
return 400000.0f;
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
float CaribouLiteRadio::GetRxSampleRateMax()
|
||||
{
|
||||
return 4000000.0f;
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
void CaribouLiteRadio::SetRxSampleRate(float sr_hz)
|
||||
{
|
||||
cariboulite_radio_set_rx_sample_rate_flt((cariboulite_radio_state_st*)_radio, sr_hz);
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
float CaribouLiteRadio::GetRxSampleRate()
|
||||
{
|
||||
float sr = 0.0f;
|
||||
cariboulite_radio_get_rx_sample_rate_flt((cariboulite_radio_state_st*)_radio, &sr);
|
||||
return sr;
|
||||
}
|
||||
|
||||
// Tx Sample Rate
|
||||
|
||||
//==================================================================
|
||||
float CaribouLiteRadio::GetTxSampleRateMin()
|
||||
{
|
||||
return 400000.0f;
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
float CaribouLiteRadio::GetTxSampleRateMax()
|
||||
{
|
||||
return 4000000.0f;
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
void CaribouLiteRadio::SetTxSampleRate(float sr_hz)
|
||||
{
|
||||
cariboulite_radio_set_tx_samp_cutoff_flt((cariboulite_radio_state_st*)_radio, sr_hz);
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
float CaribouLiteRadio::GetTxSampleRate()
|
||||
{
|
||||
float sr = 0.0f;
|
||||
cariboulite_radio_get_tx_samp_cutoff_flt((cariboulite_radio_state_st*)_radio, &sr);
|
||||
return sr;
|
||||
}
|
||||
|
||||
// RSSI and Rx Power and others
|
||||
|
||||
//==================================================================
|
||||
float CaribouLiteRadio::GetRssi()
|
||||
{
|
||||
float rssi = 0.0f;
|
||||
cariboulite_radio_get_rssi((cariboulite_radio_state_st*)_radio, &rssi);
|
||||
return rssi;
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
float CaribouLiteRadio::GetEnergyDet()
|
||||
{
|
||||
float ed = 0.0f;
|
||||
cariboulite_radio_get_energy_det((cariboulite_radio_state_st*)_radio, &ed);
|
||||
return ed;
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
unsigned char CaribouLiteRadio::GetTrueRandVal()
|
||||
{
|
||||
unsigned char val = 0;
|
||||
cariboulite_radio_get_rand_val((cariboulite_radio_state_st*)_radio, &val);
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
// Frequency Control
|
||||
|
||||
//==================================================================
|
||||
void CaribouLiteRadio::SetFrequency(float freq_hz)
|
||||
{
|
||||
if (!cariboulite_frequency_available((cariboulite_channel_en)_type, freq_hz))
|
||||
{
|
||||
char msg[128] = {0};
|
||||
sprintf(msg, "Frequency out or range %.2f Hz on %s", freq_hz, GetRadioName().c_str());
|
||||
throw std::invalid_argument(msg);
|
||||
}
|
||||
double freq_dbl = freq_hz;
|
||||
if (cariboulite_radio_set_frequency((cariboulite_radio_state_st*)_radio,
|
||||
true,
|
||||
&freq_dbl) != 0)
|
||||
{
|
||||
char msg[128] = {0};
|
||||
sprintf(msg, "Frequency setting on %s failed", GetRadioName().c_str());
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
float CaribouLiteRadio::GetFrequency()
|
||||
{
|
||||
double freq = 0;
|
||||
cariboulite_radio_get_frequency((cariboulite_radio_state_st*)_radio,
|
||||
&freq, NULL, NULL);
|
||||
return freq;
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
std::vector<CaribouLiteFreqRange> CaribouLiteRadio::GetFrequencyRange()
|
||||
{
|
||||
std::vector<CaribouLiteFreqRange> ranges;
|
||||
float freq_mins[10];
|
||||
float freq_maxs[10];
|
||||
int num = 0;
|
||||
cariboulite_get_frequency_limits((cariboulite_channel_en)_type, freq_mins, freq_maxs, &num);
|
||||
for (int i = 0; i < num; i++)
|
||||
{
|
||||
ranges.push_back(CaribouLiteFreqRange(freq_mins[i], freq_maxs[i]));
|
||||
}
|
||||
return ranges;
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
float CaribouLiteRadio::GetFrequencyResolution()
|
||||
{
|
||||
// TBD: todo calculate it according to the real hardware specs
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// Activation
|
||||
|
||||
//==================================================================
|
||||
void CaribouLiteRadio::StartReceivingInternal(size_t samples_per_chunk)
|
||||
{
|
||||
_rx_samples_per_chunk = (samples_per_chunk==0)?GetNativeMtuSample():samples_per_chunk;
|
||||
|
||||
if (_rx_samples_per_chunk > GetNativeMtuSample())
|
||||
{
|
||||
_rx_samples_per_chunk = GetNativeMtuSample();
|
||||
}
|
||||
|
||||
// make sure only one radio is receiving at once
|
||||
CaribouLiteRadio* otherRadio = ((CaribouLite*)_device)->GetRadioChannel((_type==RadioType::S1G)?(RadioType::HiF):(RadioType::S1G));
|
||||
otherRadio->StopReceiving();
|
||||
|
||||
cariboulite_radio_activate_channel((cariboulite_radio_state_st*)_radio, cariboulite_channel_dir_rx, true);
|
||||
_rx_is_active = true;
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
void CaribouLiteRadio::StartReceiving(std::function<void(CaribouLiteRadio*, const std::complex<float>*, CaribouLiteMeta*, size_t)> on_data_ready, size_t samples_per_chunk)
|
||||
{
|
||||
_on_data_ready_fm = on_data_ready;
|
||||
_rxCallbackType = RxCbType::FloatSync;
|
||||
StartReceivingInternal(samples_per_chunk);
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
void CaribouLiteRadio::StartReceiving(std::function<void(CaribouLiteRadio*, const std::complex<float>*, size_t)> on_data_ready, size_t samples_per_chunk)
|
||||
{
|
||||
_on_data_ready_f = on_data_ready;
|
||||
_rxCallbackType = RxCbType::Float;
|
||||
StartReceivingInternal(samples_per_chunk);
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
void CaribouLiteRadio::StartReceiving(std::function<void(CaribouLiteRadio*, const CaribouLiteComplexInt*, CaribouLiteMeta*, size_t)> on_data_ready, size_t samples_per_chunk)
|
||||
{
|
||||
_on_data_ready_im = on_data_ready;
|
||||
_rxCallbackType = RxCbType::IntSync;
|
||||
StartReceivingInternal(samples_per_chunk);
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
void CaribouLiteRadio::StartReceiving(std::function<void(CaribouLiteRadio*, const CaribouLiteComplexInt*, size_t)> on_data_ready, size_t samples_per_chunk)
|
||||
{
|
||||
_on_data_ready_i = on_data_ready;
|
||||
_rxCallbackType = RxCbType::Int;
|
||||
StartReceivingInternal(samples_per_chunk);
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
void CaribouLiteRadio::StopReceiving()
|
||||
{
|
||||
_rx_is_active = false;
|
||||
cariboulite_radio_activate_channel((cariboulite_radio_state_st*)_radio, cariboulite_channel_dir_rx, false);
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
void CaribouLiteRadio::StartTransmitting(std::function<void(CaribouLiteRadio*, std::complex<float>*, const bool*, size_t*)> on_data_request, size_t samples_per_chunk)
|
||||
{
|
||||
_rx_is_active = false;
|
||||
_on_data_request = on_data_request;
|
||||
_tx_samples_per_chunk = samples_per_chunk;
|
||||
cariboulite_radio_activate_channel((cariboulite_radio_state_st*)_radio, cariboulite_channel_dir_rx, false);
|
||||
cariboulite_radio_set_cw_outputs((cariboulite_radio_state_st*)_radio, false, false);
|
||||
cariboulite_radio_activate_channel((cariboulite_radio_state_st*)_radio, cariboulite_channel_dir_tx, true);
|
||||
_tx_is_active = true;
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
void CaribouLiteRadio::StartTransmittingLo()
|
||||
{
|
||||
_rx_is_active = false;
|
||||
_tx_is_active = false;
|
||||
cariboulite_radio_activate_channel((cariboulite_radio_state_st*)_radio, cariboulite_channel_dir_tx, false);
|
||||
cariboulite_radio_set_cw_outputs((cariboulite_radio_state_st*)_radio, true, false);
|
||||
cariboulite_radio_activate_channel((cariboulite_radio_state_st*)_radio, cariboulite_channel_dir_tx, true);
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
void CaribouLiteRadio::StartTransmittingCw()
|
||||
{
|
||||
_rx_is_active = false;
|
||||
_tx_is_active = false;
|
||||
cariboulite_radio_activate_channel((cariboulite_radio_state_st*)_radio, cariboulite_channel_dir_tx, false);
|
||||
cariboulite_radio_set_cw_outputs((cariboulite_radio_state_st*)_radio, false, true);
|
||||
cariboulite_radio_activate_channel((cariboulite_radio_state_st*)_radio, cariboulite_channel_dir_tx, true);
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
void CaribouLiteRadio::StopTransmitting()
|
||||
{
|
||||
_tx_is_active = false;
|
||||
cariboulite_radio_set_cw_outputs((cariboulite_radio_state_st*)_radio, false, false);
|
||||
cariboulite_radio_activate_channel((cariboulite_radio_state_st*)_radio, cariboulite_channel_dir_tx, false);
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
bool CaribouLiteRadio::GetIsTransmittingLo()
|
||||
{
|
||||
bool lo_out = false;
|
||||
cariboulite_radio_get_cw_outputs((cariboulite_radio_state_st*)_radio, &lo_out, NULL);
|
||||
return lo_out;
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
bool CaribouLiteRadio::GetIsTransmittingCw()
|
||||
{
|
||||
bool cw_out = false;
|
||||
cariboulite_radio_get_cw_outputs((cariboulite_radio_state_st*)_radio, NULL, &cw_out);
|
||||
return cw_out;
|
||||
}
|
||||
|
||||
// General
|
||||
|
||||
//==================================================================
|
||||
size_t CaribouLiteRadio::GetNativeMtuSample()
|
||||
{
|
||||
return cariboulite_radio_get_native_mtu_size_samples((cariboulite_radio_state_st*)_radio);
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
std::string CaribouLiteRadio::GetRadioName()
|
||||
{
|
||||
char name[64];
|
||||
cariboulite_get_channel_name((cariboulite_channel_en)_type, name, sizeof(name));
|
||||
return std::string(name);
|
||||
}
|
||||
|
||||
//==================================================================
|
||||
void CaribouLiteRadio::FlushBuffers()
|
||||
{
|
||||
int res = cariboulite_flush_pipeline();
|
||||
|
||||
}
|
|
@ -1,775 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include "cariboulite.h"
|
||||
#include "cariboulite_setup.h"
|
||||
|
||||
//=================================================
|
||||
typedef enum
|
||||
{
|
||||
app_selection_hard_reset_fpga = 0,
|
||||
app_selection_soft_reset_fpga,
|
||||
app_selection_versions,
|
||||
app_selection_program_fpga,
|
||||
app_selection_self_test,
|
||||
app_selection_fpga_dig_control,
|
||||
app_selection_fpga_rffe_control,
|
||||
app_selection_fpga_smi_fifo,
|
||||
app_selection_modem_tx_cw,
|
||||
app_selection_modem_rx_iq,
|
||||
app_selection_synthesizer,
|
||||
app_selection_quit = 99,
|
||||
} app_selection_en;
|
||||
|
||||
typedef void (*handle_cb)(sys_st *sys);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
app_selection_en num;
|
||||
handle_cb handle;
|
||||
char text[256];
|
||||
} app_menu_item_st;
|
||||
|
||||
static void app_hard_reset_fpga(sys_st *sys);
|
||||
static void app_soft_reset_fpga(sys_st *sys);
|
||||
static void app_versions_printout(sys_st *sys);
|
||||
static void app_fpga_programming(sys_st *sys);
|
||||
static void app_self_test(sys_st *sys);
|
||||
static void fpga_control_io(sys_st *sys);
|
||||
static void fpga_rf_control(sys_st *sys);
|
||||
static void fpga_smi_fifo(sys_st *sys);
|
||||
static void modem_tx_cw(sys_st *sys);
|
||||
static void modem_rx_iq(sys_st *sys);
|
||||
static void synthesizer(sys_st *sys);
|
||||
|
||||
//=================================================
|
||||
app_menu_item_st handles[] =
|
||||
{
|
||||
{app_selection_hard_reset_fpga, app_hard_reset_fpga, "Hard reset FPGA",},
|
||||
{app_selection_soft_reset_fpga, app_soft_reset_fpga, "Soft reset FPGA",},
|
||||
{app_selection_versions, app_versions_printout, "Print board info and versions",},
|
||||
{app_selection_program_fpga, app_fpga_programming, "Program FPGA",},
|
||||
{app_selection_self_test, app_self_test, "Perform a Self-Test",},
|
||||
{app_selection_fpga_dig_control, fpga_control_io, "FPGA Digital I/O",},
|
||||
{app_selection_fpga_rffe_control, fpga_rf_control, "FPGA RFFE control",},
|
||||
{app_selection_fpga_smi_fifo, fpga_smi_fifo, "FPGA SMI fifo status",},
|
||||
{app_selection_modem_tx_cw, modem_tx_cw, "Modem transmit CW signal",},
|
||||
{app_selection_modem_rx_iq, modem_rx_iq, "Modem receive I/Q stream",},
|
||||
{app_selection_synthesizer, synthesizer, "Synthesizer 85-4200 MHz",},
|
||||
};
|
||||
#define NUM_HANDLES (int)(sizeof(handles)/sizeof(app_menu_item_st))
|
||||
|
||||
|
||||
//=================================================
|
||||
static void app_hard_reset_fpga(sys_st *sys)
|
||||
{
|
||||
caribou_fpga_hard_reset(&sys->fpga);
|
||||
}
|
||||
|
||||
//=================================================
|
||||
static void app_soft_reset_fpga(sys_st *sys)
|
||||
{
|
||||
caribou_fpga_soft_reset(&sys->fpga);
|
||||
}
|
||||
|
||||
//=================================================
|
||||
static void app_versions_printout(sys_st *sys)
|
||||
{
|
||||
printf("Board Information (HAT)\n");
|
||||
cariboulite_print_board_info(sys, false);
|
||||
caribou_fpga_get_versions (&sys->fpga, NULL);
|
||||
at86rf215_print_version(&sys->modem);
|
||||
|
||||
printf("\nLibrary Versions:\n");
|
||||
cariboulite_lib_version_st lib_vers = {0};
|
||||
cariboulite_lib_version(&lib_vers);
|
||||
printf(" (Major, Minor, Rev): (%d, %d, %d)\n", lib_vers.major_version,
|
||||
lib_vers.minor_version,
|
||||
lib_vers.revision);
|
||||
}
|
||||
|
||||
//=================================================
|
||||
static void app_fpga_programming(sys_st *sys)
|
||||
{
|
||||
app_hard_reset_fpga(sys);
|
||||
|
||||
printf("FPGA Programming:\n");
|
||||
sys->force_fpga_reprogramming = true;
|
||||
int res = cariboulite_configure_fpga (sys, cariboulite_firmware_source_blob, NULL);
|
||||
if (res < 0)
|
||||
{
|
||||
printf(" ERROR: FPGA programming failed `%d`\n", res);
|
||||
return;
|
||||
}
|
||||
printf(" FPGA programming successful, Versions:\n");
|
||||
|
||||
caribou_fpga_soft_reset(&sys->fpga);
|
||||
io_utils_usleep(100000);
|
||||
|
||||
caribou_fpga_get_versions (&sys->fpga, NULL);
|
||||
|
||||
caribou_fpga_set_io_ctrl_mode (&sys->fpga, 0, caribou_fpga_io_ctrl_rfm_low_power);
|
||||
}
|
||||
|
||||
//=================================================
|
||||
static void app_self_test(sys_st *sys)
|
||||
{
|
||||
cariboulite_self_test_result_st res = {0};
|
||||
cariboulite_self_test(sys, &res);
|
||||
}
|
||||
|
||||
//=================================================
|
||||
static void fpga_control_io(sys_st *sys)
|
||||
{
|
||||
int choice = 0;
|
||||
int led0 = 0, led1 = 0, btn = 0, cfg = 0;
|
||||
while (1)
|
||||
{
|
||||
caribou_fpga_get_io_ctrl_dig (&sys->fpga, &led0, &led1, &btn, &cfg);
|
||||
printf("\n FPGA Digital I/O state:\n");
|
||||
printf(" LED0 = %d, LED1 = %d, BTN = %d, CFG = (%d, %d, %d, %d)\n",
|
||||
led0, led1, btn,
|
||||
(cfg >> 3) & (0x1 == 1),
|
||||
(cfg >> 2) & (0x1 == 1),
|
||||
(cfg >> 1) & (0x1 == 1),
|
||||
(cfg >> 0) & (0x1 == 1));
|
||||
|
||||
printf(" [1] Toggle LED0\n [2] Toggle LED1\n [99] Return to Menu\n Choice:");
|
||||
if (scanf("%d", &choice) != 1) continue;
|
||||
switch(choice)
|
||||
{
|
||||
case 1:
|
||||
led0 = !led0;
|
||||
caribou_fpga_set_io_ctrl_dig (&sys->fpga, led0, led1);
|
||||
break;
|
||||
case 2:
|
||||
led1 = !led1;
|
||||
caribou_fpga_set_io_ctrl_dig (&sys->fpga, led0, led1);
|
||||
break;
|
||||
case 99: return;
|
||||
default: continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=================================================
|
||||
static void fpga_rf_control(sys_st *sys)
|
||||
{
|
||||
int choice = 0;
|
||||
uint8_t debug = 0;
|
||||
caribou_fpga_io_ctrl_rfm_en mode;
|
||||
while (1)
|
||||
{
|
||||
caribou_fpga_get_io_ctrl_mode (&sys->fpga, &debug, &mode);
|
||||
printf("\n FPGA RFFE state:\n");
|
||||
printf(" DEBUG = %d, MODE: '%s'\n", debug, caribou_fpga_get_mode_name (mode));
|
||||
|
||||
printf(" Available Modes:\n");
|
||||
for (int i=caribou_fpga_io_ctrl_rfm_low_power; i<=caribou_fpga_io_ctrl_rfm_tx_hipass; i++)
|
||||
{
|
||||
printf(" [%d] %s\n", i, caribou_fpga_get_mode_name (i));
|
||||
}
|
||||
printf(" [99] Return to main menu\n");
|
||||
printf("\n Choose a new mode: ");
|
||||
if (scanf("%d", &choice) != 1) continue;
|
||||
|
||||
if (choice == 99) return;
|
||||
if (choice <caribou_fpga_io_ctrl_rfm_low_power || choice >caribou_fpga_io_ctrl_rfm_tx_hipass)
|
||||
{
|
||||
printf(" Wrong choice '%d'\n", choice);
|
||||
continue;
|
||||
}
|
||||
|
||||
caribou_fpga_set_io_ctrl_mode (&sys->fpga, 0, (caribou_fpga_io_ctrl_rfm_en)choice);
|
||||
}
|
||||
}
|
||||
|
||||
//=================================================
|
||||
static void fpga_smi_fifo(sys_st *sys)
|
||||
{
|
||||
caribou_fpga_smi_fifo_status_st status = {0};
|
||||
uint8_t *val = (uint8_t *)&status;
|
||||
caribou_fpga_get_smi_ctrl_fifo_status (&sys->fpga, &status);
|
||||
|
||||
printf(" FPGA SMI info (%02X):\n", *val);
|
||||
printf(" RX FIFO EMPTY: %d\n", status.rx_fifo_empty);
|
||||
printf(" TX FIFO FULL: %d\n", status.tx_fifo_full);
|
||||
printf(" RX CHANNEL: %d\n", status.smi_channel);
|
||||
printf(" RX SMI TEST: %d\n", status.i_smi_test);
|
||||
}
|
||||
|
||||
//=================================================
|
||||
static void modem_tx_cw(sys_st *sys)
|
||||
{
|
||||
double current_freq_lo = 900e6;
|
||||
double current_freq_hi = 2400e6;
|
||||
float current_power_lo = -12;
|
||||
float current_power_hi = -12;
|
||||
|
||||
int state_lo = 0;
|
||||
int state_hi = 0;
|
||||
int choice = 0;
|
||||
|
||||
cariboulite_radio_state_st *radio_low = &sys->radio_low;
|
||||
cariboulite_radio_state_st *radio_hi = &sys->radio_high;
|
||||
|
||||
// output power
|
||||
cariboulite_radio_set_tx_power(radio_low, current_power_lo);
|
||||
cariboulite_radio_set_tx_power(radio_hi, current_power_hi);
|
||||
|
||||
// frequency
|
||||
cariboulite_radio_set_frequency(radio_low, true, ¤t_freq_lo);
|
||||
cariboulite_radio_set_frequency(radio_hi, true, ¤t_freq_hi);
|
||||
|
||||
// deactivate - just to be sure
|
||||
cariboulite_radio_activate_channel(radio_low, cariboulite_channel_dir_tx, false);
|
||||
cariboulite_radio_activate_channel(radio_hi, cariboulite_channel_dir_tx, false);
|
||||
|
||||
// setup cw outputs from modem
|
||||
cariboulite_radio_set_cw_outputs(radio_low, false, true);
|
||||
cariboulite_radio_set_cw_outputs(radio_hi, false, true);
|
||||
|
||||
// synchronize
|
||||
cariboulite_radio_sync_information(radio_low);
|
||||
cariboulite_radio_sync_information(radio_hi);
|
||||
|
||||
// update params
|
||||
current_freq_lo = radio_low->actual_rf_frequency;
|
||||
current_freq_hi = radio_hi->actual_rf_frequency;
|
||||
current_power_lo = radio_low->tx_power;
|
||||
current_power_hi = radio_hi->tx_power;
|
||||
|
||||
state_lo = radio_low->state == cariboulite_radio_state_cmd_rx;
|
||||
state_hi = radio_hi->state == cariboulite_radio_state_cmd_rx;
|
||||
|
||||
while (1)
|
||||
{
|
||||
printf(" Parameters:\n");
|
||||
printf(" [1] Frequency @ Low Channel [%.2f MHz]\n", current_freq_lo);
|
||||
printf(" [2] Frequency @ High Channel [%.2f MHz]\n", current_freq_hi);
|
||||
printf(" [3] Power out @ Low Channel [%.2f dBm]\n", current_power_lo);
|
||||
printf(" [4] Power out @ High Channel [%.2f dBm]\n", current_power_hi);
|
||||
printf(" [5] On/off CW output @ Low Channel [Currently %s]\n", state_lo?"ON":"OFF");
|
||||
printf(" [6] On/off CW output @ High Channel [Currently %s]\n", state_hi?"ON":"OFF");
|
||||
printf(" [7] Low Channel decrease frequency (5MHz)\n");
|
||||
printf(" [8] Low Channel increase frequency (5MHz)\n");
|
||||
printf(" [9] Hi Channel decrease frequency (5MHz)\n");
|
||||
printf(" [10] Hi Channel increase frequency (5MHz)\n");
|
||||
printf(" [99] Return to Main Menu\n");
|
||||
printf(" Choice: ");
|
||||
if (scanf("%d", &choice) != 1) continue;
|
||||
|
||||
switch (choice)
|
||||
{
|
||||
//---------------------------------------------------------
|
||||
case 1:
|
||||
{
|
||||
printf(" Enter frequency @ Low Channel [Hz]: ");
|
||||
if (scanf("%lf", ¤t_freq_lo) != 1) continue;
|
||||
|
||||
cariboulite_radio_activate_channel(radio_low, cariboulite_channel_dir_tx, false);
|
||||
cariboulite_radio_set_frequency(radio_low, true, ¤t_freq_lo);
|
||||
cariboulite_radio_set_tx_power(radio_low, current_power_lo);
|
||||
if (state_lo)
|
||||
{
|
||||
cariboulite_radio_activate_channel(radio_low, cariboulite_channel_dir_tx, true);
|
||||
}
|
||||
current_freq_lo = radio_low->actual_rf_frequency;
|
||||
}
|
||||
break;
|
||||
|
||||
//---------------------------------------------------------
|
||||
case 2:
|
||||
{
|
||||
printf(" Enter frequency @ High Channel [Hz]: ");
|
||||
if (scanf("%lf", ¤t_freq_hi) != 1) continue;
|
||||
|
||||
cariboulite_radio_activate_channel(radio_hi, cariboulite_channel_dir_tx, false);
|
||||
cariboulite_radio_set_frequency(radio_hi, true, ¤t_freq_hi);
|
||||
cariboulite_radio_set_tx_power(radio_hi, current_power_hi);
|
||||
|
||||
if (state_hi)
|
||||
{
|
||||
cariboulite_radio_activate_channel(radio_hi, cariboulite_channel_dir_tx, true);
|
||||
}
|
||||
current_freq_hi = radio_hi->actual_rf_frequency;
|
||||
}
|
||||
break;
|
||||
|
||||
//---------------------------------------------------------
|
||||
case 3:
|
||||
{
|
||||
printf(" Enter power @ Low Channel [dBm]: ");
|
||||
if (scanf("%f", ¤t_power_lo) != 1) continue;
|
||||
|
||||
cariboulite_radio_set_tx_power(radio_low, current_power_lo);
|
||||
current_power_lo = radio_low->tx_power;
|
||||
}
|
||||
break;
|
||||
|
||||
//---------------------------------------------------------
|
||||
case 4:
|
||||
{
|
||||
printf(" Enter power @ High Channel [dBm]: ");
|
||||
if (scanf("%f", ¤t_power_hi) != 1) continue;
|
||||
|
||||
cariboulite_radio_set_tx_power(radio_hi, current_power_hi);
|
||||
current_power_hi = radio_hi->tx_power;
|
||||
}
|
||||
break;
|
||||
|
||||
//---------------------------------------------------------
|
||||
case 5:
|
||||
{
|
||||
state_lo = !state_lo;
|
||||
if (state_lo == 1) cariboulite_radio_set_tx_power(radio_low, current_power_lo);
|
||||
cariboulite_radio_set_cw_outputs(radio_low, false, state_lo);
|
||||
cariboulite_radio_activate_channel(radio_low, cariboulite_channel_dir_tx, state_lo);
|
||||
}
|
||||
break;
|
||||
|
||||
//---------------------------------------------------------
|
||||
case 6:
|
||||
{
|
||||
state_hi = !state_hi;
|
||||
if (state_hi == 1) cariboulite_radio_set_tx_power(radio_hi, current_power_hi);
|
||||
cariboulite_radio_set_cw_outputs(radio_hi, false, state_hi);
|
||||
cariboulite_radio_activate_channel(radio_hi, cariboulite_channel_dir_tx, state_hi);
|
||||
}
|
||||
break;
|
||||
|
||||
//---------------------------------------------------------
|
||||
case 7:
|
||||
{
|
||||
current_freq_lo -= 5e6;
|
||||
cariboulite_radio_activate_channel(radio_low, cariboulite_channel_dir_tx, false);
|
||||
cariboulite_radio_set_frequency(radio_low, true, ¤t_freq_lo);
|
||||
cariboulite_radio_set_tx_power(radio_low, current_power_lo);
|
||||
if (state_lo)
|
||||
{
|
||||
cariboulite_radio_activate_channel(radio_low, cariboulite_channel_dir_tx, true);
|
||||
}
|
||||
//current_freq_lo = radio_low->actual_rf_frequency;
|
||||
}
|
||||
break;
|
||||
|
||||
//---------------------------------------------------------
|
||||
case 8:
|
||||
{
|
||||
current_freq_lo += 5e6;
|
||||
cariboulite_radio_activate_channel(radio_low, cariboulite_channel_dir_tx, false);
|
||||
cariboulite_radio_set_frequency(radio_low, true, ¤t_freq_lo);
|
||||
cariboulite_radio_set_tx_power(radio_low, current_power_lo);
|
||||
if (state_lo)
|
||||
{
|
||||
cariboulite_radio_activate_channel(radio_low, cariboulite_channel_dir_tx, true);
|
||||
}
|
||||
//current_freq_lo = radio_low->actual_rf_frequency;
|
||||
}
|
||||
break;
|
||||
|
||||
//---------------------------------------------------------
|
||||
case 9:
|
||||
{
|
||||
current_freq_hi -= 5e6;
|
||||
cariboulite_radio_activate_channel(radio_hi, cariboulite_channel_dir_tx, false);
|
||||
cariboulite_radio_set_frequency(radio_hi, true, ¤t_freq_hi);
|
||||
cariboulite_radio_set_tx_power(radio_hi, current_power_hi);
|
||||
|
||||
if (state_hi)
|
||||
{
|
||||
cariboulite_radio_activate_channel(radio_hi, cariboulite_channel_dir_tx, true);
|
||||
}
|
||||
//current_freq_hi = radio_hi->actual_rf_frequency;
|
||||
}
|
||||
break;
|
||||
|
||||
//---------------------------------------------------------
|
||||
case 10:
|
||||
{
|
||||
current_freq_hi += 5e6;
|
||||
cariboulite_radio_activate_channel(radio_hi, cariboulite_channel_dir_tx, false);
|
||||
cariboulite_radio_set_frequency(radio_hi, true, ¤t_freq_hi);
|
||||
cariboulite_radio_set_tx_power(radio_hi, current_power_hi);
|
||||
|
||||
if (state_hi)
|
||||
{
|
||||
cariboulite_radio_activate_channel(radio_hi, cariboulite_channel_dir_tx, true);
|
||||
}
|
||||
//current_freq_hi = radio_hi->actual_rf_frequency;
|
||||
}
|
||||
break;
|
||||
|
||||
//---------------------------------------------------------
|
||||
case 99:
|
||||
{
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
//---------------------------------------------------------
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=================================================
|
||||
typedef struct
|
||||
{
|
||||
bool active;
|
||||
sys_st *sys;
|
||||
|
||||
cariboulite_radio_state_st *radio_low;
|
||||
cariboulite_radio_state_st *radio_hi;
|
||||
|
||||
bool *low_active;
|
||||
bool *high_active;
|
||||
} iq_test_reader_st;
|
||||
|
||||
static void print_iq(char* prefix, cariboulite_sample_complex_int16* buffer, size_t num_samples, int num_head_tail)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (num_samples == 0) return;
|
||||
|
||||
printf("NS: %lu > ", num_samples);
|
||||
|
||||
for (i = 0; i < num_head_tail; i++)
|
||||
{
|
||||
printf("[%d, %d] ", buffer[i].i, buffer[i].q);
|
||||
}
|
||||
printf(". . . ");
|
||||
for (i = num_samples-num_head_tail; i < (int)num_samples; i++)
|
||||
{
|
||||
printf("[%d, %d] ", buffer[i].i, buffer[i].q);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void* reader_thread_func(void* arg)
|
||||
{
|
||||
iq_test_reader_st* ctrl = (iq_test_reader_st*)arg;
|
||||
cariboulite_radio_state_st *cur_radio = NULL;
|
||||
size_t read_len = caribou_smi_get_native_batch_samples(&ctrl->sys->smi);
|
||||
|
||||
// allocate buffer
|
||||
cariboulite_sample_complex_int16* buffer = malloc(sizeof(cariboulite_sample_complex_int16)*read_len);
|
||||
cariboulite_sample_meta* metadata = malloc(sizeof(cariboulite_sample_meta)*read_len);
|
||||
|
||||
printf("Entering sampling thread\n");
|
||||
while (ctrl->active)
|
||||
{
|
||||
if (*ctrl->low_active)
|
||||
{
|
||||
cur_radio = ctrl->radio_low;
|
||||
}
|
||||
else if (*ctrl->high_active)
|
||||
{
|
||||
cur_radio = ctrl->radio_hi;
|
||||
}
|
||||
else
|
||||
{
|
||||
cur_radio = NULL;
|
||||
usleep(10000);
|
||||
}
|
||||
|
||||
if (cur_radio)
|
||||
{
|
||||
int ret = cariboulite_radio_read_samples(cur_radio, buffer, metadata, read_len);
|
||||
if (ret < 0)
|
||||
{
|
||||
if (ret == -1)
|
||||
{
|
||||
printf("reader thread failed to read SMI!\n");
|
||||
}
|
||||
}
|
||||
else print_iq("Rx", buffer, ret, 4);
|
||||
}
|
||||
}
|
||||
printf("Leaving sampling thread\n");
|
||||
free(buffer);
|
||||
free(metadata);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void modem_rx_iq(sys_st *sys)
|
||||
{
|
||||
int choice = 0;
|
||||
bool low_active_rx = false;
|
||||
bool high_active_rx = false;
|
||||
bool push_debug = false;
|
||||
bool pull_debug = false;
|
||||
bool lfsr_debug = false;
|
||||
double current_freq_lo = 900e6;
|
||||
double current_freq_hi = 2400e6;
|
||||
|
||||
iq_test_reader_st ctrl = {0};
|
||||
|
||||
// create the radio
|
||||
cariboulite_radio_state_st *radio_low = &sys->radio_low;
|
||||
cariboulite_radio_state_st *radio_hi = &sys->radio_high;
|
||||
|
||||
ctrl.active = true;
|
||||
ctrl.radio_low = radio_low;
|
||||
ctrl.radio_hi = radio_hi;
|
||||
ctrl.sys = sys;
|
||||
ctrl.low_active = &low_active_rx;
|
||||
ctrl.high_active = &high_active_rx;
|
||||
|
||||
// start the reader thread
|
||||
pthread_t reader_thread;
|
||||
if (pthread_create(&reader_thread, NULL, &reader_thread_func, &ctrl) != 0)
|
||||
{
|
||||
printf("reader thread creation failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// frequency
|
||||
cariboulite_radio_set_frequency(radio_low, true, ¤t_freq_lo);
|
||||
cariboulite_radio_set_frequency(radio_hi, true, ¤t_freq_hi);
|
||||
|
||||
// synchronize
|
||||
cariboulite_radio_sync_information(radio_low);
|
||||
cariboulite_radio_sync_information(radio_hi);
|
||||
|
||||
cariboulite_radio_activate_channel(radio_low, cariboulite_channel_dir_rx, false);
|
||||
cariboulite_radio_activate_channel(radio_hi, cariboulite_channel_dir_rx, false);
|
||||
caribou_smi_set_debug_mode(&sys->smi, caribou_smi_none);
|
||||
|
||||
while (1)
|
||||
{
|
||||
printf(" Parameters:\n");
|
||||
printf(" [1] Ch1 (%.5f MHz) RX %s\n", current_freq_lo / 1e6, low_active_rx?"Active":"Not Active");
|
||||
printf(" [2] Ch2 (%.5f MHz) RX %s\n", current_freq_hi / 1e6, high_active_rx?"Active":"Not Active");
|
||||
printf(" [3] Push Debug %s\n", push_debug?"Active":"Not Active");
|
||||
printf(" [4] Pull Debug %s\n", pull_debug?"Active":"Not Active");
|
||||
printf(" [5] LFSR Debug %s\n", lfsr_debug?"Active":"Not Active");
|
||||
printf(" [99] Return to main menu\n");
|
||||
|
||||
printf(" Choice: ");
|
||||
if (scanf("%d", &choice) != 1) continue;
|
||||
|
||||
switch (choice)
|
||||
{
|
||||
//--------------------------------------------
|
||||
case 1:
|
||||
{
|
||||
if (!low_active_rx && high_active_rx)
|
||||
{
|
||||
// if high is currently active - deactivate it
|
||||
high_active_rx = false;
|
||||
printf("Turning on Low channel => High channel off\n");
|
||||
cariboulite_radio_activate_channel(radio_hi, cariboulite_channel_dir_rx, false);
|
||||
}
|
||||
|
||||
low_active_rx = !low_active_rx;
|
||||
cariboulite_radio_activate_channel(radio_low, cariboulite_channel_dir_rx, low_active_rx);
|
||||
}
|
||||
break;
|
||||
|
||||
//--------------------------------------------
|
||||
case 2:
|
||||
{
|
||||
if (!high_active_rx && low_active_rx)
|
||||
{
|
||||
// if low is currently active - deactivate it
|
||||
low_active_rx = false;
|
||||
printf("Turning on High channel => Low channel off\n");
|
||||
cariboulite_radio_activate_channel(radio_low, cariboulite_channel_dir_rx, false);
|
||||
}
|
||||
|
||||
high_active_rx = !high_active_rx;
|
||||
cariboulite_radio_activate_channel(radio_hi, cariboulite_channel_dir_rx, high_active_rx);
|
||||
}
|
||||
break;
|
||||
|
||||
//--------------------------------------------
|
||||
case 3:
|
||||
{
|
||||
push_debug = !push_debug;
|
||||
|
||||
if (push_debug)
|
||||
{
|
||||
pull_debug = false;
|
||||
lfsr_debug = false;
|
||||
caribou_smi_set_debug_mode(&sys->smi, caribou_smi_push);
|
||||
}
|
||||
else caribou_smi_set_debug_mode(&sys->smi, caribou_smi_none);
|
||||
|
||||
caribou_fpga_set_debug_modes (&sys->fpga, push_debug, pull_debug, lfsr_debug);
|
||||
}
|
||||
break;
|
||||
|
||||
//--------------------------------------------
|
||||
case 4:
|
||||
{
|
||||
pull_debug = !pull_debug;
|
||||
|
||||
if (pull_debug)
|
||||
{
|
||||
push_debug = false;
|
||||
lfsr_debug = false;
|
||||
caribou_smi_set_debug_mode(&sys->smi, caribou_smi_pull);
|
||||
}
|
||||
else caribou_smi_set_debug_mode(&sys->smi, caribou_smi_none);
|
||||
|
||||
caribou_fpga_set_debug_modes (&sys->fpga, push_debug, pull_debug, lfsr_debug);
|
||||
}
|
||||
break;
|
||||
|
||||
//--------------------------------------------
|
||||
case 5:
|
||||
{
|
||||
lfsr_debug = !lfsr_debug;
|
||||
|
||||
if (lfsr_debug)
|
||||
{
|
||||
push_debug = false;
|
||||
pull_debug = false;
|
||||
caribou_smi_set_debug_mode(&sys->smi, caribou_smi_lfsr);
|
||||
}
|
||||
else caribou_smi_set_debug_mode(&sys->smi, caribou_smi_none);
|
||||
|
||||
caribou_fpga_set_debug_modes (&sys->fpga, push_debug, pull_debug, lfsr_debug);
|
||||
}
|
||||
break;
|
||||
|
||||
//--------------------------------------------
|
||||
case 99:
|
||||
low_active_rx = false;
|
||||
high_active_rx = false;
|
||||
ctrl.active = false;
|
||||
pthread_join(reader_thread, NULL);
|
||||
|
||||
cariboulite_radio_activate_channel(radio_low, cariboulite_channel_dir_rx, false);
|
||||
cariboulite_radio_activate_channel(radio_hi, cariboulite_channel_dir_rx, false);
|
||||
return;
|
||||
|
||||
//--------------------------------------------
|
||||
default:
|
||||
{
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=================================================
|
||||
static void synthesizer(sys_st *sys)
|
||||
{
|
||||
int choice = 0;
|
||||
cariboulite_radio_state_st *radio_hi = &sys->radio_high;
|
||||
double current_freq = 100000000.0;
|
||||
bool active = false;
|
||||
bool lock = false;
|
||||
|
||||
//cariboulite_radio_set_cw_outputs(radio_hi, false, false);
|
||||
//cariboulite_radio_activate_channel(radio_hi, cariboulite_channel_dir_tx, false);
|
||||
caribou_fpga_set_io_ctrl_mode (&radio_hi->sys->fpga, 0, caribou_fpga_io_ctrl_rfm_tx_lowpass);
|
||||
cariboulite_radio_ext_ref (radio_hi->sys, cariboulite_ext_ref_32mhz);
|
||||
rffc507x_set_frequency(&radio_hi->sys->mixer, current_freq);
|
||||
lock = cariboulite_radio_wait_mixer_lock(radio_hi, 10);
|
||||
rffc507x_calibrate(&radio_hi->sys->mixer);
|
||||
|
||||
while (1)
|
||||
{
|
||||
printf(" Parameters:\n");
|
||||
printf(" [1] Set Frequency (%.5f MHz, LOCKED=%d)\n", current_freq / 1e6, lock);
|
||||
printf(" [2] Activate [%s]\n", active?"Active":"Not Active");
|
||||
printf(" [99] Return to main menu\n");
|
||||
|
||||
printf(" Choice: ");
|
||||
if (scanf("%d", &choice) != 1) continue;
|
||||
|
||||
switch (choice)
|
||||
{
|
||||
//--------------------------------------------
|
||||
case 1:
|
||||
{
|
||||
printf(" Enter frequency [Hz]: ");
|
||||
if (scanf("%lf", ¤t_freq) != 1) continue;
|
||||
|
||||
if (current_freq < 2400e6) caribou_fpga_set_io_ctrl_mode (&radio_hi->sys->fpga, 0, caribou_fpga_io_ctrl_rfm_tx_lowpass);
|
||||
else caribou_fpga_set_io_ctrl_mode (&radio_hi->sys->fpga, 0, caribou_fpga_io_ctrl_rfm_tx_hipass);
|
||||
|
||||
double act_freq = rffc507x_set_frequency(&radio_hi->sys->mixer, current_freq);
|
||||
lock = cariboulite_radio_wait_mixer_lock(radio_hi, 10);
|
||||
|
||||
if (active)
|
||||
{
|
||||
rffc507x_output_lo(&radio_hi->sys->mixer, active);
|
||||
}
|
||||
current_freq = act_freq;
|
||||
}
|
||||
break;
|
||||
|
||||
//--------------------------------------------
|
||||
case 2:
|
||||
{
|
||||
active = !active;
|
||||
rffc507x_output_lo(&radio_hi->sys->mixer, active);
|
||||
}
|
||||
break;
|
||||
|
||||
//--------------------------------------------
|
||||
case 99:
|
||||
active = false;
|
||||
|
||||
rffc507x_output_lo(&radio_hi->sys->mixer, false);
|
||||
cariboulite_radio_set_cw_outputs(radio_hi, false, false);
|
||||
caribou_fpga_set_io_ctrl_mode (&radio_hi->sys->fpga, 0, caribou_fpga_io_ctrl_rfm_bypass);
|
||||
return;
|
||||
|
||||
//--------------------------------------------
|
||||
default:
|
||||
{
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//=================================================
|
||||
int app_menu(sys_st* sys)
|
||||
{
|
||||
printf("\n");
|
||||
printf(" ____ _ _ _ _ _ \n");
|
||||
printf(" / ___|__ _ _ __(_) |__ ___ _ _| | (_) |_ ___ \n");
|
||||
printf(" | | / _` | '__| | '_ \\ / _ \\| | | | | | | __/ _ \\ \n");
|
||||
printf(" | |__| (_| | | | | |_) | (_) | |_| | |___| | || __/ \n");
|
||||
printf(" \\____\\__,_|_| |_|_.__/ \\___/ \\__,_|_____|_|\\__\\___| \n");
|
||||
printf("\n\n");
|
||||
|
||||
while (1)
|
||||
{
|
||||
int choice = -1;
|
||||
printf(" Select a function:\n");
|
||||
for (int i = 0; i < NUM_HANDLES; i++)
|
||||
{
|
||||
printf(" [%d] %s\n", handles[i].num, handles[i].text);
|
||||
}
|
||||
printf(" [%d] %s\n", app_selection_quit, "Quit");
|
||||
|
||||
printf(" Choice: ");
|
||||
if (scanf("%d", &choice) != 1) continue;
|
||||
|
||||
if ((app_selection_en)(choice) == app_selection_quit) return 0;
|
||||
for (int i = 0; i < NUM_HANDLES; i++)
|
||||
{
|
||||
if (handles[i].num == (app_selection_en)(choice))
|
||||
{
|
||||
if (handles[i].handle != NULL)
|
||||
{
|
||||
printf("\n=====================================\n");
|
||||
handles[i].handle(sys);
|
||||
printf("\n=====================================\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf(" Choice %d is not implemented\n", choice);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
# build directories
|
||||
build
|
|
@ -1,24 +0,0 @@
|
|||
cmake_minimum_required(VERSION 3.15)
|
||||
project(cariboulite)
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
|
||||
#Bring the headers, such as Student.h into the project
|
||||
set(SUPER_DIR ${PROJECT_SOURCE_DIR}/..)
|
||||
include_directories(/.)
|
||||
include_directories(${SUPER_DIR})
|
||||
|
||||
#However, the file(GLOB...) allows for wildcard additions:
|
||||
set(SOURCES_LIB at86rf215.c at86rf215_events.c at86rf215_radio.c at86rf215_baseband.c)
|
||||
set(SOURCES ${SOURCES_LIB} test_at86rf215.c)
|
||||
set(EXTERN_LIBS ${SUPER_DIR}/io_utils/build/libio_utils.a ${SUPER_DIR}/zf_log/build/libzf_log.a -lpthread)
|
||||
#add_compile_options(-Wall -Wextra -pedantic -Werror)
|
||||
add_compile_options(-Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -Wno-missing-braces)
|
||||
|
||||
#Generate the static library from the sources
|
||||
add_library(at86rf215 STATIC ${SOURCES_LIB})
|
||||
#add_executable(test_at86rf215 ${SOURCES})
|
||||
#target_link_libraries(test_at86rf215 rt pthread ${EXTERN_LIBS})
|
||||
|
||||
#Set the location for library installation -- i.e., /usr/lib in this case
|
||||
# not really necessary in this example. Use "sudo make install" to apply
|
||||
install(TARGETS at86rf215 DESTINATION /usr/lib)
|
|
@ -1,771 +0,0 @@
|
|||
#ifndef ZF_LOG_LEVEL
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#endif
|
||||
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "AT86RF215_Main"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include "zf_log/zf_log.h"
|
||||
#include "at86rf215.h"
|
||||
#include "io_utils/io_utils.h"
|
||||
#include "io_utils/io_utils_spi.h"
|
||||
#include "at86rf215_radio.h"
|
||||
#include "at86rf215_regs.h"
|
||||
|
||||
//===================================================================
|
||||
int at86rf215_write_buffer(at86rf215_st* dev, uint16_t addr, uint8_t *buffer, uint8_t size )
|
||||
{
|
||||
// a maximal possible chunk size - 256 + 2(addr)
|
||||
uint8_t chunk_tx[258] = {0};
|
||||
uint8_t chunk_rx[258] = {0};
|
||||
chunk_tx[0] = ((addr >> 8) & 0x3F) | 0x80;
|
||||
chunk_tx[1] = addr & 0xFF;
|
||||
memcpy(chunk_tx + 2, buffer, size);
|
||||
|
||||
return io_utils_spi_transmit(dev->io_spi, dev->io_spi_handle, chunk_tx, chunk_rx, size + 2, io_utils_spi_read_write);
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
int at86rf215_read_buffer(at86rf215_st* dev, uint16_t addr, uint8_t *buffer, uint8_t size)
|
||||
{
|
||||
// a maximal possible chunk size - 256 + 2(addr)
|
||||
uint8_t chunk_tx[258] = {0};
|
||||
uint8_t chunk_rx[258] = {0};
|
||||
chunk_tx[0] = (addr >> 8) & 0x3F;
|
||||
chunk_tx[1] = addr & 0xFF;
|
||||
|
||||
int ret = io_utils_spi_transmit(dev->io_spi, dev->io_spi_handle, chunk_tx, chunk_rx, size + 2, io_utils_spi_read_write);
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
memcpy(buffer, chunk_rx + 2, size);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
int at86rf215_write_byte(at86rf215_st* dev, uint16_t addr, uint8_t val )
|
||||
{
|
||||
uint8_t chunk_tx[3] = {0};
|
||||
uint8_t chunk_rx[3] = {0};
|
||||
chunk_tx[0] = ((addr >> 8) & 0x3F) | 0x80;
|
||||
chunk_tx[1] = addr & 0xFF;
|
||||
chunk_tx[2] = val;
|
||||
return io_utils_spi_transmit(dev->io_spi, dev->io_spi_handle,
|
||||
chunk_tx, chunk_rx, 3, io_utils_spi_read_write);
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
int at86rf215_read_byte(at86rf215_st* dev, uint16_t addr)
|
||||
{
|
||||
uint8_t chunk_tx[3] = {0};
|
||||
uint8_t chunk_rx[3] = {0};
|
||||
|
||||
chunk_tx[0] = (addr >> 8) & 0x3F;
|
||||
chunk_tx[1] = addr & 0xFF;
|
||||
|
||||
/*printf("TX: ");
|
||||
for (int i = 0; i < 3; i ++)
|
||||
printf(" 0x%02X ", chunk_tx[i]);
|
||||
printf("\n");*/
|
||||
|
||||
int ret = io_utils_spi_transmit(dev->io_spi, dev->io_spi_handle,
|
||||
chunk_tx, chunk_rx, 3, io_utils_spi_read_write);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*printf("RX: ");
|
||||
for (int i = 0; i < 3; i ++)
|
||||
printf(" 0x%02X ", chunk_rx[i]);
|
||||
printf("\n");*/
|
||||
|
||||
return chunk_rx[2];
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
#define NUM_CAL_STEPS 5
|
||||
void swap(int *p,int *q)
|
||||
{
|
||||
int t;
|
||||
t=*p;
|
||||
*p=*q;
|
||||
*q=t;
|
||||
}
|
||||
|
||||
void sort(int a[],int n)
|
||||
{
|
||||
int i,j,temp;
|
||||
|
||||
for(i = 0;i < n-1;i++) {
|
||||
for(j = 0;j < n-i-1;j++) {
|
||||
if(a[j] > a[j+1])
|
||||
swap(&a[j],&a[j+1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int median(int a[], int n)
|
||||
{
|
||||
if (n==0) return 0;
|
||||
sort(a,n);
|
||||
return a[(n+1)/2-1];
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
int at86rf215_calibrate_device(at86rf215_st* dev, at86rf215_rf_channel_en ch, int* i_val, int* q_val)
|
||||
{
|
||||
int cal_i[NUM_CAL_STEPS] = {0};
|
||||
int cal_q[NUM_CAL_STEPS] = {0};
|
||||
bool override_flag = dev->override_cal;
|
||||
dev->override_cal = false;
|
||||
|
||||
ZF_LOGD("Calibration of modem channel %d...", ch);
|
||||
for (int i = 0; i < NUM_CAL_STEPS; i ++)
|
||||
{
|
||||
at86rf215_radio_set_state(dev, ch, at86rf215_radio_state_cmd_trx_off);
|
||||
io_utils_usleep(2000);
|
||||
at86rf215_radio_set_state(dev, ch, at86rf215_radio_state_cmd_tx_prep);
|
||||
|
||||
io_utils_usleep(10000);
|
||||
|
||||
at86rf215_radio_get_tx_iq_calibration(dev, ch, &cal_i[i], &cal_q[i]);
|
||||
//printf("[%d,%d], ", cal_i[i], cal_q[i]);
|
||||
}
|
||||
|
||||
// medians
|
||||
int cal_i_med = median(cal_i, NUM_CAL_STEPS);
|
||||
int cal_q_med = median(cal_q, NUM_CAL_STEPS);
|
||||
ZF_LOGD("Calibration Results of the modem: I=%d, Q=%d", cal_i_med, cal_q_med);
|
||||
if (i_val) *i_val = cal_i_med;
|
||||
if (q_val) *q_val = cal_q_med;
|
||||
if (ch == at86rf215_rf_channel_900mhz)
|
||||
{
|
||||
dev->cal.low_ch_i = cal_i_med;
|
||||
dev->cal.low_ch_q = cal_q_med;
|
||||
}
|
||||
if (ch == at86rf215_rf_channel_2400mhz)
|
||||
{
|
||||
dev->cal.hi_ch_i = cal_i_med;
|
||||
dev->cal.hi_ch_q = cal_q_med;
|
||||
}
|
||||
dev->override_cal = override_flag;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
int at86rf215_init(at86rf215_st* dev,
|
||||
io_utils_spi_st* io_spi)
|
||||
{
|
||||
if (dev == NULL)
|
||||
{
|
||||
ZF_LOGE("dev is NULL");
|
||||
return -1;
|
||||
}
|
||||
|
||||
dev->io_spi = io_spi;
|
||||
|
||||
ZF_LOGD("configuring reset and irq pins");
|
||||
// Configure GPIO pins
|
||||
io_utils_setup_gpio(dev->reset_pin, io_utils_dir_output, io_utils_pull_off);
|
||||
io_utils_setup_gpio(dev->irq_pin, io_utils_dir_input, io_utils_pull_up);
|
||||
|
||||
// set to known state
|
||||
io_utils_write_gpio(dev->reset_pin, 1);
|
||||
|
||||
ZF_LOGD("Adding chip definition to io_utils_spi");
|
||||
io_utils_hard_spi_st hard_dev_modem = { .spi_dev_id = dev->spi_dev, .spi_dev_channel = dev->spi_channel, };
|
||||
dev->io_spi_handle = io_utils_spi_add_chip(dev->io_spi, dev->cs_pin, 1000000, 0, 0,
|
||||
io_utils_spi_chip_type_modem,
|
||||
&hard_dev_modem);
|
||||
|
||||
// Setup the interrupts after clearing the register one time
|
||||
at86rf215_irq_st irq = {0};
|
||||
at86rf215_get_irqs(dev, &irq, 0);
|
||||
|
||||
dev->num_interrupts = 0;
|
||||
if (io_utils_setup_interrupt(dev->irq_pin, at86rf215_interrupt_handler, dev) < 0)
|
||||
{
|
||||
ZF_LOGE("interrupt registration for irq_pin (%d) failed", dev->irq_pin);
|
||||
//io_utils_setup_gpio(dev->reset_pin, io_utils_dir_input, io_utils_pull_up);
|
||||
io_utils_setup_gpio(dev->irq_pin, io_utils_dir_input, io_utils_pull_up);
|
||||
io_utils_spi_remove_chip(dev->io_spi, dev->io_spi_handle);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Initialize events
|
||||
event_node_init(&dev->events.lo_trx_ready_event);
|
||||
event_node_init(&dev->events.lo_energy_measure_event);
|
||||
event_node_init(&dev->events.hi_trx_ready_event);
|
||||
event_node_init(&dev->events.hi_energy_measure_event);
|
||||
|
||||
// Get chip type
|
||||
uint8_t pn = 0, vn = 0;
|
||||
at86rf215_get_versions(dev, &pn, &vn);
|
||||
ZF_LOGD("Modem identity: Version: %02X, Product: %02X", vn, pn);
|
||||
|
||||
// calibrate TXPREP
|
||||
at86rf215_calibrate_device(dev, at86rf215_rf_channel_900mhz, &dev->cal.low_ch_i, &dev->cal.low_ch_q);
|
||||
at86rf215_calibrate_device(dev, at86rf215_rf_channel_2400mhz, &dev->cal.hi_ch_i, &dev->cal.hi_ch_q);
|
||||
dev->override_cal = true;
|
||||
dev->initialized = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//===================================================================
|
||||
int at86rf215_close(at86rf215_st* dev)
|
||||
{
|
||||
if (dev == NULL)
|
||||
{
|
||||
ZF_LOGE("device pointer NULL");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!dev->initialized)
|
||||
{
|
||||
ZF_LOGE("device not initialized");
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev->initialized = 0;
|
||||
|
||||
event_node_close(&dev->events.lo_trx_ready_event);
|
||||
event_node_close(&dev->events.lo_energy_measure_event);
|
||||
event_node_close(&dev->events.hi_trx_ready_event);
|
||||
event_node_close(&dev->events.hi_energy_measure_event);
|
||||
|
||||
//io_utils_setup_gpio(dev->reset_pin, io_utils_dir_input, io_utils_pull_up);
|
||||
io_utils_setup_gpio(dev->irq_pin, io_utils_dir_input, io_utils_pull_up);
|
||||
|
||||
// Release the SPI device
|
||||
io_utils_spi_remove_chip(dev->io_spi, dev->io_spi_handle);
|
||||
|
||||
ZF_LOGD("device release completed");
|
||||
return 0;
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
void at86rf215_reset(at86rf215_st* dev)
|
||||
{
|
||||
io_utils_write_gpio(dev->reset_pin, 0);
|
||||
io_utils_usleep(300);
|
||||
io_utils_write_gpio(dev->reset_pin, 1);
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
void at86rf215_chip_reset_with_spi(at86rf215_st* dev)
|
||||
{
|
||||
uint8_t val = 0x7;
|
||||
at86rf215_write_byte(dev, REG_RF_RST, val);
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
void at86rf215_get_versions(at86rf215_st* dev, uint8_t *pn, uint8_t *vn)
|
||||
{
|
||||
if (pn) *pn = at86rf215_read_byte(dev, REG_RF_PN);
|
||||
if (vn) *vn = at86rf215_read_byte(dev, REG_RF_VN);
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
int at86rf215_print_version(at86rf215_st* dev)
|
||||
{
|
||||
uint8_t pn = 0, vn = 0;
|
||||
at86rf215_get_versions(dev, &pn, &vn);
|
||||
//at86rf215_get_versions(dev, &pn, &vn);
|
||||
|
||||
if (pn == at86rf215_pn_at86rf215) // 0x34
|
||||
{
|
||||
ZF_LOGD("MODEM Version: AT86RF215 (with basebands), version: %02x", vn);
|
||||
}
|
||||
else if (pn == at86rf215_pn_at86rf215iq) // 0x35
|
||||
{
|
||||
ZF_LOGD("MODEM Version: AT86RF215IQ (without basebands), version: %02x", vn);
|
||||
}
|
||||
else
|
||||
{
|
||||
ZF_LOGW("MODEM Version: not AT86RF215 IQ capable modem (product number: 0x%02x, versions %02x)", pn, vn);
|
||||
}
|
||||
return pn;
|
||||
}
|
||||
|
||||
|
||||
//===================================================================
|
||||
int at86rf215_write_fifo(at86rf215_st* dev, uint8_t *buffer, uint8_t size )
|
||||
{
|
||||
return at86rf215_write_buffer(dev, 0, buffer, size);
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
int at86rf215_read_fifo(at86rf215_st* dev, uint8_t *buffer, uint8_t size )
|
||||
{
|
||||
return at86rf215_read_buffer(dev, 0, buffer, size);
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
void at86rf215_set_clock_output(at86rf215_st* dev,
|
||||
at86rf215_drive_current_en drv_level,
|
||||
at86rf215_clock_out_freq_en clock_val)
|
||||
{
|
||||
uint8_t val = ( (drv_level&0x03)<<3 ) | (clock_val&0x07);
|
||||
at86rf215_write_byte(dev, REG_RF_CLKO, val);
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
void at86rf215_setup_rf_irq(at86rf215_st* dev, uint8_t active_low,
|
||||
uint8_t show_masked_irq,
|
||||
at86rf215_drive_current_en drive)
|
||||
{
|
||||
uint8_t val = 0;
|
||||
val |= (show_masked_irq&0x1)<<3;
|
||||
val |= (active_low&0x1)<<2;
|
||||
val |= (drive&0x3);
|
||||
at86rf215_write_byte(dev, REG_RF_CFG, val);
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
static void at86rf215_print_radio_irq(at86rf215_radio_irq_st* irq)
|
||||
{
|
||||
printf(" IQ_if_sync_fail: %d\n", irq->IQ_if_sync_fail);
|
||||
printf(" trx_error: %d\n", irq->trx_error);
|
||||
printf(" battery_low: %d\n", irq->battery_low);
|
||||
printf(" energy_detection_complete: %d\n", irq->energy_detection_complete);
|
||||
printf(" trx_ready: %d\n", irq->trx_ready);
|
||||
printf(" wake_up_por: %d\n", irq->wake_up_por);
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
static void at86rf215_print_bb_irq(at86rf215_baseband_irq_st* irq)
|
||||
{
|
||||
printf(" frame_buffer_level: %d\n", irq->frame_buffer_level);
|
||||
printf(" agc_release: %d\n", irq->agc_release);
|
||||
printf(" agc_hold: %d\n", irq->agc_hold);
|
||||
printf(" frame_tx_complete: %d\n", irq->frame_tx_complete);
|
||||
printf(" frame_rx_match_extended: %d\n", irq->frame_rx_match_extended);
|
||||
printf(" frame_rx_address_match: %d\n", irq->frame_rx_address_match);
|
||||
printf(" frame_rx_complete: %d\n", irq->frame_rx_complete);
|
||||
printf(" frame_rx_started: %d\n", irq->frame_rx_started);
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
void at86rf215_get_irqs(at86rf215_st* dev, at86rf215_irq_st* irq, int verbose)
|
||||
{
|
||||
at86rf215_read_buffer(dev, REG_RF09_IRQS, (uint8_t*)irq, 4);
|
||||
if (verbose)
|
||||
{
|
||||
printf("IRQ Status:\n");
|
||||
printf(" Radio09:\n"); at86rf215_print_radio_irq(&irq->radio09);
|
||||
printf(" Radio24:\n"); at86rf215_print_radio_irq(&irq->radio24);
|
||||
printf(" Baseband09:\n"); at86rf215_print_bb_irq(&irq->bb0);
|
||||
printf(" Baseband24:\n"); at86rf215_print_bb_irq(&irq->bb1);
|
||||
}
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
void at86rf215_set_xo_trim(at86rf215_st* dev, uint8_t fast_start, float cap_trim)
|
||||
{
|
||||
if (cap_trim < 0.0f) cap_trim = 0.0f;
|
||||
if (cap_trim > 4.5f) cap_trim = 4.5f;
|
||||
uint8_t trim_val = ((uint8_t)((cap_trim+0.1f)/0.3f))&0xF;
|
||||
trim_val |= (fast_start&0x1)<<4;
|
||||
at86rf215_write_byte(dev, REG_RF_XOC, trim_val);
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
void at86rf215_get_iq_if_cfg(at86rf215_st* dev, at86rf215_iq_interface_config_st* cfg, int verbose)
|
||||
{
|
||||
uint8_t data[3] = {0};
|
||||
at86rf215_read_buffer(dev, REG_RF_IQIFC0, data, 3);
|
||||
cfg->loopback_enable = (data[0]>>7)&0x01;
|
||||
cfg->synchronization_failed = (data[0]>>6)&0x01;
|
||||
cfg->drv_strength = (data[0]>>4)&0x03;
|
||||
uint8_t cmv = (data[0]>>2)&0x03;
|
||||
cfg->common_mode_voltage = ((data[0]>>1)&0x01)?at86rf215_iq_common_mode_v_ieee1596_1v2:cmv;
|
||||
cfg->tx_control_with_iq_if = (data[0]>>0)&0x01;
|
||||
cfg->in_failsafe_mode = (data[1]>>7)&0x01;
|
||||
uint8_t chip_mode = (data[1]>>4)&0x07;
|
||||
|
||||
switch (chip_mode)
|
||||
{
|
||||
case 0x00:
|
||||
cfg->radio09_mode = at86rf215_baseband_mode;
|
||||
cfg->radio24_mode = at86rf215_baseband_mode;
|
||||
break;
|
||||
case 0x01:
|
||||
cfg->radio09_mode = at86rf215_iq_if_mode;
|
||||
cfg->radio24_mode = at86rf215_iq_if_mode;
|
||||
break;
|
||||
case 0x04:
|
||||
cfg->radio09_mode = at86rf215_iq_if_mode;
|
||||
cfg->radio24_mode = at86rf215_baseband_mode;
|
||||
break;
|
||||
case 0x05:
|
||||
cfg->radio09_mode = at86rf215_baseband_mode;
|
||||
cfg->radio24_mode = at86rf215_iq_if_mode;
|
||||
break;
|
||||
default:
|
||||
ZF_LOGE("I/Q chipmode invalid: %d", chip_mode);
|
||||
break;
|
||||
}
|
||||
|
||||
cfg->clock_skew = (data[1]>>0)&0x03;
|
||||
cfg->synchronized_incoming_iq = (data[2]>>7)&0x01;
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
printf("Current I/Q interface settings:\n");
|
||||
printf(" Loopback (RX => TX): %s\n", cfg->loopback_enable?"enabled":"disabled");
|
||||
printf(" Drive strength: %d mA\n", cfg->drv_strength+1);
|
||||
|
||||
if (cfg->common_mode_voltage == at86rf215_iq_common_mode_v_ieee1596_1v2)
|
||||
{
|
||||
printf(" Common mode voltage: %.1f V\n", 1.2f);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf(" Common mode voltage: %d mV\n", (cfg->common_mode_voltage+1) * 50 + 100);
|
||||
}
|
||||
|
||||
printf(" I/Q interface for sub-GHz: %s\n", cfg->radio09_mode==at86rf215_iq_if_mode?"enabled":"diabled");
|
||||
printf(" I/Q interface for 2.4-GHz: %s\n", cfg->radio24_mode==at86rf215_iq_if_mode?"enabled":"diabled");
|
||||
printf(" I/Q Clock <=> Data skew: %.3f ns\n", cfg->clock_skew+1.906f);
|
||||
|
||||
printf(" Status 'Sync Failure': %d\n", cfg->synchronization_failed);
|
||||
printf(" Status 'Failsafe mode': %d\n", cfg->in_failsafe_mode);
|
||||
printf(" Status 'Is synchronized to incoming I/Q': %d\n", cfg->synchronized_incoming_iq);
|
||||
}
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
void at86rf215_setup_iq_if(at86rf215_st* dev, at86rf215_iq_interface_config_st* cfg)
|
||||
{
|
||||
uint8_t data[2] = {0};
|
||||
data[0] |= (cfg->loopback_enable&0x01) << 7;
|
||||
data[0] |= (cfg->drv_strength&0x03) << 4;
|
||||
|
||||
if (cfg->common_mode_voltage == at86rf215_iq_common_mode_v_ieee1596_1v2)
|
||||
{
|
||||
data[0] |= 1<<1;
|
||||
}
|
||||
else
|
||||
{
|
||||
data[0] |= (cfg->common_mode_voltage & 0x3) << 2;
|
||||
}
|
||||
data[0] |= (cfg->tx_control_with_iq_if & 0x01) << 0;
|
||||
|
||||
if (cfg->radio09_mode == at86rf215_iq_if_mode && cfg->radio24_mode == at86rf215_iq_if_mode)
|
||||
{
|
||||
data[1] |= 0x01 << 4;
|
||||
}
|
||||
else if (cfg->radio09_mode == at86rf215_iq_if_mode && cfg->radio24_mode == at86rf215_baseband_mode)
|
||||
{
|
||||
data[1] |= 0x04 << 4;
|
||||
}
|
||||
else if (cfg->radio09_mode == at86rf215_baseband_mode && cfg->radio24_mode == at86rf215_iq_if_mode)
|
||||
{
|
||||
data[1] |= 0x05 << 4;
|
||||
}
|
||||
|
||||
data[1] |= (cfg->clock_skew & 0x03) << 0;
|
||||
|
||||
at86rf215_write_buffer(dev, REG_RF_IQIFC0, data, 2);
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
double at86rf215_check_freq (at86rf215_st* dev, at86rf215_rf_channel_en ch, uint64_t freq_hz )
|
||||
{
|
||||
at86rf215_radio_channel_mode_en mode = 0;
|
||||
at86rf215_rf_channel_en req_ch = 0;
|
||||
|
||||
if (at86rf215_radio_get_good_channel(freq_hz, &mode, &req_ch) < 0 || req_ch != ch)
|
||||
{
|
||||
ZF_LOGE("the requested channel or frequency not supported");
|
||||
return -1;
|
||||
}
|
||||
int center_freq_25khz_res = 0;
|
||||
int channel_number = 0;
|
||||
double actual_freq = at86rf215_radio_get_frequency(mode, 1, freq_hz, ¢er_freq_25khz_res, &channel_number);
|
||||
return actual_freq;
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
int64_t at86rf215_setup_channel ( at86rf215_st* dev, at86rf215_rf_channel_en ch, uint64_t freq_hz )
|
||||
{
|
||||
if (dev->initialized == 0)
|
||||
{
|
||||
ZF_LOGE("device not initialized");
|
||||
return -1;
|
||||
}
|
||||
|
||||
at86rf215_radio_channel_mode_en mode = 0;
|
||||
at86rf215_rf_channel_en req_ch = 0;
|
||||
|
||||
if (at86rf215_radio_get_good_channel(freq_hz, &mode, &req_ch) < 0 || req_ch != ch)
|
||||
{
|
||||
ZF_LOGE("the requested channel or frequency not supported");
|
||||
return -1;
|
||||
}
|
||||
int center_freq_25khz_res = 0;
|
||||
int channel_number = 0;
|
||||
double actual_freq = at86rf215_radio_get_frequency(mode, 1, freq_hz, ¢er_freq_25khz_res, &channel_number);
|
||||
at86rf215_radio_setup_channel(dev, ch, 1, center_freq_25khz_res, channel_number, mode);
|
||||
return (int64_t)actual_freq;
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
void at86rf215_setup_iq_radio_transmit (at86rf215_st* dev, at86rf215_rf_channel_en radio)
|
||||
{
|
||||
/*
|
||||
It is assumed, that the radio has been reset before and is in State TRXOFF. All interrupts in register RFn_IRQS should be enabled (RFn_IRQM=0x3f).
|
||||
*/
|
||||
|
||||
// 1. Set TRXOFF mode
|
||||
// 2. Enable all interrupts in 09,_24_IRQS
|
||||
// 3. Enable I/Q radio mode - setting IQIFC1.CHPM=1 at AT86RF215
|
||||
// 4. Configure the Transmitter Frontend:
|
||||
// Set the transmitter analog frontend sub-registers TXCUTC.LPFCUT and TXCUTC.PARAMP
|
||||
// Set the transmitter digital frontend sub-registers TXDFE.SR and TXDFE.RCUT
|
||||
// 5. Configure the channel parameters, see section "Channel Configuration" on page 62 and transmit power
|
||||
// 6. Optional: Perform ED measurement, see section "Energy Measurement" on page 56. The following steps are recommended:
|
||||
// Configure the measurement period, see register RFn_EDD.
|
||||
// Switch to State RX.
|
||||
// Start and finish a measurement:
|
||||
// For single and continuous ED modes a measurement starts if the mode is written to sub-register EDC.EDM.
|
||||
// The completion of the measurement is indicated by the interrupt IRQS.EDC. The resulting ED value can be read from register RFn_EDV.
|
||||
// For the automatic mode, a measurement starts by setting bit AGCC.FRZC=1. After the completion of the measurement period, the ED value can be read from register RFn_EDV.
|
||||
// 7. Switch to State TXPREP; interrupt IRQS.TRXRDY is issued.
|
||||
// 8. To start the actual transmission, there are two possibilities, depending on the setting of sub-register IQIFC0.EEC:
|
||||
// IQIFC0.EEC=0 => Enable the radio transmitter by writing command TX to the register RFn_CMD via SPI.
|
||||
// IQIFC0.EEC=1 => The transmitter is activated automatically with the TX start signal embedded in I_DATA[0],
|
||||
// 9. To finish a transmission depends on the setting of bit IQIFC0.EEC:
|
||||
// IQIFC0.EEC=0 => To leave the State TX, write command TXPREP to the register RFn_CMD. Reaching State TXPREP is indicated by the interrupt IRQS.TRXRDY.
|
||||
// IQIFC0.EEC=1 => If the bit I_DATA[0] is set to 0 (see Figure 6-4 on page 47) the ramp down process of the PA is started automatically. After ramp down the transmitter switches back to State TXPREP.
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
void at86rf215_setup_iq_radio_receive (at86rf215_st* dev, at86rf215_rf_channel_en radio, uint64_t freq_hz,
|
||||
int iqloopback, at86rf215_iq_clock_data_skew_en skew)
|
||||
{
|
||||
/*
|
||||
It is assumed, that
|
||||
1. the radio has been reset before and is in State TRXOFF.
|
||||
2. All interrupts in register RFn_IRQS should be enabled (RFn_IRQM=0x3f).
|
||||
*/
|
||||
|
||||
// 1. Set TRXOFF mode
|
||||
at86rf215_radio_set_state(dev, radio, at86rf215_radio_state_cmd_trx_off);
|
||||
|
||||
|
||||
// 2. Enable all radio interrupts in 09,_24_IRQS
|
||||
at86rf215_radio_irq_st int_mask = {
|
||||
.wake_up_por = 1,
|
||||
.trx_ready = 1,
|
||||
.energy_detection_complete = 1,
|
||||
.battery_low = 1,
|
||||
.trx_error = 1,
|
||||
.IQ_if_sync_fail = 1,
|
||||
.res = 0,
|
||||
};
|
||||
at86rf215_radio_setup_interrupt_mask(dev, radio, &int_mask);
|
||||
|
||||
// 3. Enable I/Q radio mode - setting IQIFC1.CHPM=1 at AT86RF215 (in AT86RF215IQ it is the only choice)
|
||||
at86rf215_iq_interface_config_st iq_if_config =
|
||||
{
|
||||
.loopback_enable = iqloopback,
|
||||
.drv_strength = at86rf215_iq_drive_current_4ma,
|
||||
.common_mode_voltage = at86rf215_iq_common_mode_v_ieee1596_1v2,
|
||||
.tx_control_with_iq_if = false,
|
||||
.radio09_mode = at86rf215_iq_if_mode,
|
||||
.radio24_mode = at86rf215_iq_if_mode,
|
||||
.clock_skew = skew,
|
||||
};
|
||||
at86rf215_setup_iq_if(dev, &iq_if_config);
|
||||
|
||||
// 4. Configure the Receiving Frontend:
|
||||
// Set the receiver analog frontend sub-registers RXBWC.BW and RXBWC.IFS,
|
||||
// Set the receiver digital frontend sub-registers RXDFE.SR and RXDFE.RCUT
|
||||
// Set the AGC registers RFn_AGCC and RFn_AGCS
|
||||
at86rf215_radio_set_rx_bw_samp_st rx_bw_samp_cfg =
|
||||
{
|
||||
.inverter_sign_if = 0, // A value of one configures the receiver to implement the inverted-sign IF freq.
|
||||
.shift_if_freq = 0, // A value of one configures the receiver to shift the IF frequency by factor of 1.25.
|
||||
.bw = at86rf215_radio_rx_bw_BW2000KHZ_IF2000KHZ,
|
||||
// The sub-register controls the receiver filter bandwidth settings.
|
||||
.fcut = at86rf215_radio_rx_f_cut_half_fs,
|
||||
// RX filter relative cut-off frequency
|
||||
.fs = at86rf215_radio_rx_sample_rate_4000khz,
|
||||
// RX Sample Rate
|
||||
};
|
||||
at86rf215_radio_set_rx_bandwidth_sampling(dev, radio, &rx_bw_samp_cfg);
|
||||
|
||||
at86rf215_radio_agc_ctrl_st agc_ctrl =
|
||||
{
|
||||
// commands
|
||||
.agc_measure_source_not_filtered = 0, // AGC Input (0 - filterred, 1 - unfiltered, faster operation)
|
||||
.avg = at86rf215_radio_agc_averaging_8, // AGC Average Time in Number of Samples
|
||||
.reset_cmd = 0, // AGC Reset - resets the AGC and sets the maximum receiver gain.
|
||||
.freeze_cmd = 0, // AGC Freeze Control - A value of one forces the AGC to
|
||||
// freeze to its current value.
|
||||
.enable_cmd = 1, // AGC Enable - a value of zero allows a manual setting of
|
||||
// the RX gain control by sub-register AGCS.GCW
|
||||
.att = at86rf215_radio_agc_relative_atten_21_db,// AGC Target Level - sets the AGC target level relative to ADC full scale.
|
||||
.gain_control_word = 0, // If AGCC_EN is set to 1, a read of bit AGCS.GCW indicates the current
|
||||
// receiver gain setting. If AGCC_EN is set to 0, a write access to GCW
|
||||
// manually sets the receiver gain. An integer value of 23 indicates
|
||||
// the maximum receiver gain; each integer step changes the gain by 3dB.
|
||||
.freeze_status = 0, // AGC Freeze Status - A value of one indicates that the AGC is on hold.
|
||||
};
|
||||
at86rf215_radio_setup_agc(dev, radio, &agc_ctrl);
|
||||
|
||||
// 5. Configure the channel parameters, see section "Channel Configuration" on page 62 and transmit power
|
||||
at86rf215_setup_channel (dev, radio, freq_hz);
|
||||
|
||||
// 6. Switch to State TXPREP; interrupt IRQS.TRXRDY is issued.
|
||||
// TXD and TXCLK are activated as shown in Figure 4-12 on page 26.
|
||||
// What? Why TX?
|
||||
|
||||
// 7. Prepare the external baseband for reception of I/Q samples
|
||||
// The FPGA was born prepared (;
|
||||
|
||||
// 8. Enable the radio receiver by writing command RX to the register RFn_CMD.
|
||||
at86rf215_radio_set_state(dev, radio, at86rf215_radio_state_cmd_rx);
|
||||
|
||||
// 9. To prevent the AGC from switching its gain during reception, it is recommended to set AGCC.FRZC=1
|
||||
// after reception of the preamble, the AGC has to be released after finishing reception by setting AGCC.FRZC=0.
|
||||
// at86rf215_radio_setup_agc(dev, radio, &agc_ctrl);
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
void at86rf215_stop_iq_radio_receive (at86rf215_st* dev, at86rf215_rf_channel_en radio)
|
||||
{
|
||||
at86rf215_radio_set_state(dev, radio, at86rf215_radio_state_cmd_trx_off);
|
||||
at86rf215_iq_interface_config_st iq_if_config =
|
||||
{
|
||||
.loopback_enable = 0,
|
||||
.drv_strength = at86rf215_iq_drive_current_2ma,
|
||||
.common_mode_voltage = at86rf215_iq_common_mode_v_ieee1596_1v2,
|
||||
.tx_control_with_iq_if = false,
|
||||
.radio09_mode = at86rf215_baseband_mode,
|
||||
.radio24_mode = at86rf215_baseband_mode,
|
||||
.clock_skew = at86rf215_iq_clock_data_skew_4_906ns,
|
||||
};
|
||||
at86rf215_setup_iq_if(dev, &iq_if_config);
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
void at86rf215_setup_iq_radio_continues_tx (at86rf215_st* dev, at86rf215_rf_channel_en ch)
|
||||
{
|
||||
// This is useful for application / production tests as well as certification tests.
|
||||
// Using this mode, the transceiver acts as a continuous transmitter
|
||||
// 1. Prior to transmission, the AT86RF215 must be in state TXPREP, see section "State Machine" on page 33.
|
||||
// 2. The continuous transmission is enabled if the sub-register PC.CTX is set to 1
|
||||
// 3. A frame transmission, started by CMD.CMD=TX with enabled continuous transmit mode (PC.CTX),
|
||||
// transmits synchronization header (SHR), PHY header (PHR) and repeatedly PHY payload (PSDU).
|
||||
// 4. The current PHY settings are used
|
||||
// 5. the length of the PHY payload is configured by the concatenation of the registers BBCn_TXFLH and BBCn_TXFLL.
|
||||
// If the sub-register PC.TXAFCS is set to 1, the last PHY payload octets are replaced by the calculated FCS
|
||||
// 6. The transmission proceeds as long as the sub-register PC.CTX remains 1. If the sub-register PC.CTX is set
|
||||
// to 0, the transmission stops once the current PSDU transmission is completed
|
||||
}
|
||||
|
||||
void at86rf215_setup_iq_radio_dac_value_override_no_freq (at86rf215_st* dev,
|
||||
at86rf215_rf_channel_en ch,
|
||||
uint8_t tx_power)
|
||||
{
|
||||
at86rf215_radio_state_cmd_en state = at86rf215_radio_get_state(dev, ch);
|
||||
if (state != at86rf215_radio_state_cmd_trx_off)
|
||||
{
|
||||
at86rf215_radio_set_state(dev, ch, at86rf215_radio_state_cmd_trx_off);
|
||||
}
|
||||
at86rf215_radio_set_state(dev, ch, at86rf215_radio_state_cmd_tx_prep);
|
||||
|
||||
at86rf215_radio_tx_ctrl_st tx_config =
|
||||
{
|
||||
.pa_ramping_time = at86rf215_radio_tx_pa_ramp_32usec,
|
||||
.current_reduction = at86rf215_radio_pa_current_reduction_0ma,
|
||||
.tx_power = tx_power,
|
||||
.analog_bw = at86rf215_radio_tx_cut_off_80khz,
|
||||
.digital_bw = at86rf215_radio_rx_f_cut_half_fs,
|
||||
.fs = at86rf215_radio_rx_sample_rate_4000khz,
|
||||
.direct_modulation = 0,
|
||||
};
|
||||
at86rf215_radio_setup_tx_ctrl(dev, ch, &tx_config);
|
||||
|
||||
at86rf215_radio_set_tx_dac_input_iq(dev, ch, 1, 0x7E, 1, 0x3F);
|
||||
at86rf215_radio_set_state(dev, ch, at86rf215_radio_state_cmd_tx);
|
||||
}
|
||||
|
||||
|
||||
void at86rf215_setup_iq_radio_dac_value_override (at86rf215_st* dev,
|
||||
at86rf215_rf_channel_en ch,
|
||||
uint32_t freq_hz,
|
||||
uint8_t tx_power)
|
||||
{
|
||||
// The AT86RF215 comprises a DAC (digital to analog converter) value overwrite functionality.
|
||||
// Each transceiver contains two transmitter DACs in order to transmit IQ signals. This feature is useful to
|
||||
// transmit an LO carrier which is necessary for certain certifications.
|
||||
// If the sub-register TXDACI.ENTXDACID is set to 1, the digital input value of the in-phase signal DAC is
|
||||
// overwritten with the value of the sub-register TXDACI.TXDACID. The same with TXDACQ.ENTXDACQD and TXDACQ.TXDACQD.
|
||||
|
||||
// Both TXDACI.TXDACID and TXDACQ.TXDACQD contain 7-bit binary values in the range from 0x00 to 0x7E (dec. 126).
|
||||
// 0x3F (63) is zero, 0x00 is the minimal signal, and 0x7E is the maximal signal.
|
||||
// If the transceiver is in state TX and both DAC values are overwritten, the transceiver transmits an LO
|
||||
// carrier of the frequency selected by the "Frequency Synthesizer (PLL)" on page 62.
|
||||
// To start a continuous transmission of a LO carrier, the transmitter is started as described in
|
||||
// (at86rf215_setup_iq_radio_continues_tx) Frame Based Continuous Transmission.
|
||||
// Alternatively, the transmitter can be started using chip mode 1 if sub-register IQIFC1.CHPM is set to 0x01.
|
||||
// (this is the case of I/Q over LVDS) In this case the transmitter is started by command TX.
|
||||
|
||||
at86rf215_radio_state_cmd_en state = at86rf215_radio_get_state(dev, ch);
|
||||
if (state != at86rf215_radio_state_cmd_trx_off)
|
||||
{
|
||||
at86rf215_radio_set_state(dev, ch, at86rf215_radio_state_cmd_trx_off);
|
||||
}
|
||||
at86rf215_radio_set_state(dev, ch, at86rf215_radio_state_cmd_tx_prep);
|
||||
|
||||
at86rf215_radio_tx_ctrl_st tx_config =
|
||||
{
|
||||
.pa_ramping_time = at86rf215_radio_tx_pa_ramp_32usec,
|
||||
.current_reduction = at86rf215_radio_pa_current_reduction_0ma,
|
||||
.tx_power = tx_power,
|
||||
.analog_bw = at86rf215_radio_tx_cut_off_80khz,
|
||||
.digital_bw = at86rf215_radio_rx_f_cut_half_fs,
|
||||
.fs = at86rf215_radio_rx_sample_rate_4000khz,
|
||||
.direct_modulation = 0,
|
||||
};
|
||||
at86rf215_radio_setup_tx_ctrl(dev, ch, &tx_config);
|
||||
|
||||
at86rf215_radio_external_ctrl_st aux_cfg =
|
||||
{
|
||||
.ext_lna_bypass_available = 0,
|
||||
.agc_backoff = 0,
|
||||
.analog_voltage_external = 0,
|
||||
.analog_voltage_enable_in_off = 0,
|
||||
.int_power_amplifier_voltage = 2,
|
||||
.fe_pad_configuration = 1,
|
||||
};
|
||||
|
||||
at86rf215_radio_setup_external_settings(dev, ch, &aux_cfg);
|
||||
|
||||
at86rf215_iq_interface_config_st iq_if_config =
|
||||
{
|
||||
.loopback_enable = 0,
|
||||
.drv_strength = at86rf215_iq_drive_current_2ma,
|
||||
.common_mode_voltage = at86rf215_iq_common_mode_v_ieee1596_1v2,
|
||||
.tx_control_with_iq_if = false,
|
||||
.radio09_mode = at86rf215_iq_if_mode,
|
||||
.radio24_mode = at86rf215_iq_if_mode,
|
||||
.clock_skew = at86rf215_iq_clock_data_skew_4_906ns,
|
||||
};
|
||||
at86rf215_setup_iq_if(dev, &iq_if_config);
|
||||
at86rf215_radio_set_tx_dac_input_iq(dev, ch, 1, 0x7E, 1, 0x3F);
|
||||
at86rf215_setup_channel (dev, ch, freq_hz);
|
||||
at86rf215_radio_set_state(dev, ch, at86rf215_radio_state_cmd_tx);
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
#ifndef __AT86RF215_H__
|
||||
#define __AT86RF215_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "at86rf215_common.h"
|
||||
#include "at86rf215_radio.h"
|
||||
|
||||
|
||||
int at86rf215_init(at86rf215_st* dev,
|
||||
io_utils_spi_st* io_spi);
|
||||
int at86rf215_close(at86rf215_st* dev);
|
||||
void at86rf215_reset(at86rf215_st* dev);
|
||||
|
||||
void at86rf215_get_versions(at86rf215_st* dev, uint8_t *pn, uint8_t *vn);
|
||||
int at86rf215_print_version(at86rf215_st* dev);
|
||||
void at86rf215_set_clock_output(at86rf215_st* dev,
|
||||
at86rf215_drive_current_en drv_level,
|
||||
at86rf215_clock_out_freq_en clock_val);
|
||||
void at86rf215_setup_rf_irq(at86rf215_st* dev, uint8_t active_low,
|
||||
uint8_t show_masked_irq,
|
||||
at86rf215_drive_current_en drive);
|
||||
void at86rf215_set_xo_trim(at86rf215_st* dev, uint8_t fast_start, float cap_trim);
|
||||
void at86rf215_get_iq_if_cfg(at86rf215_st* dev, at86rf215_iq_interface_config_st* cfg, int verbose);
|
||||
void at86rf215_setup_iq_if(at86rf215_st* dev, at86rf215_iq_interface_config_st* cfg);
|
||||
void at86rf215_setup_iq_radio_transmit (at86rf215_st* dev, at86rf215_rf_channel_en radio);
|
||||
void at86rf215_setup_iq_radio_receive(at86rf215_st *dev, at86rf215_rf_channel_en radio, uint64_t freq_hz,
|
||||
int iqloopback, at86rf215_iq_clock_data_skew_en skew);
|
||||
void at86rf215_stop_iq_radio_receive (at86rf215_st* dev, at86rf215_rf_channel_en radio);
|
||||
void at86rf215_setup_iq_radio_continues_tx (at86rf215_st* dev, at86rf215_rf_channel_en radio);
|
||||
void at86rf215_setup_iq_radio_dac_value_override (at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
uint32_t freq_hz,
|
||||
uint8_t tx_power );
|
||||
void at86rf215_setup_iq_radio_dac_value_override_no_freq (at86rf215_st* dev,
|
||||
at86rf215_rf_channel_en ch,
|
||||
uint8_t tx_power);
|
||||
int64_t at86rf215_setup_channel ( at86rf215_st* dev, at86rf215_rf_channel_en ch, uint64_t freq_hz );
|
||||
double at86rf215_check_freq (at86rf215_st* dev, at86rf215_rf_channel_en ch, uint64_t freq_hz );
|
||||
|
||||
// EVENTS
|
||||
|
||||
void event_node_init(event_st* ev);
|
||||
void event_node_close(event_st* ev);
|
||||
void event_node_wait_ready(event_st* ev);
|
||||
void event_node_signal_ready(event_st* ev, int ready);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __AT86RF215_H__
|
|
@ -1,213 +0,0 @@
|
|||
#ifndef ZF_LOG_LEVEL
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#endif
|
||||
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "AT86RF215_BaseBand"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include "zf_log/zf_log.h"
|
||||
#include "io_utils/io_utils.h"
|
||||
#include "io_utils/io_utils_spi.h"
|
||||
#include "at86rf215_radio.h"
|
||||
#include "at86rf215_baseband.h"
|
||||
#include "at86rf215_regs.h"
|
||||
|
||||
static const struct at86rf215_BBC_regs BBC0_regs = {
|
||||
.RG_IRQS = 0x02,
|
||||
.RG_FBRXS = 0x2000,
|
||||
.RG_FBRXE = 0x27FE,
|
||||
.RG_FBTXS = 0x2800,
|
||||
.RG_FBTXE = 0x2FFE,
|
||||
.RG_IRQM = 0x300,
|
||||
.RG_PC = 0x301,
|
||||
.RG_PS = 0x302,
|
||||
.RG_RXFLL = 0x304,
|
||||
.RG_RXFLH = 0x305,
|
||||
.RG_TXFLL = 0x306,
|
||||
.RG_TXFLH = 0x307,
|
||||
.RG_FBLL = 0x308,
|
||||
.RG_FBLH = 0x309,
|
||||
.RG_FBLIL = 0x30A,
|
||||
.RG_FBLIH = 0x30B,
|
||||
.RG_OFDMPHRTX = 0x30C,
|
||||
.RG_OFDMPHRRX = 0x30D,
|
||||
.RG_OFDMC = 0x30E,
|
||||
.RG_OFDMSW = 0x30F,
|
||||
.RG_OQPSKC0 = 0x310,
|
||||
.RG_OQPSKC1 = 0x311,
|
||||
.RG_OQPSKC2 = 0x312,
|
||||
.RG_OQPSKC3 = 0x313,
|
||||
.RG_OQPSKPHRTX = 0x314,
|
||||
.RG_OQPSKPHRRX = 0x315,
|
||||
.RG_AFC0 = 0x320,
|
||||
.RG_AFC1 = 0x321,
|
||||
.RG_AFFTM = 0x322,
|
||||
.RG_AFFVM = 0x323,
|
||||
.RG_AFS = 0x324,
|
||||
.RG_MACEA0 = 0x325,
|
||||
.RG_MACEA1 = 0x326,
|
||||
.RG_MACEA2 = 0x327,
|
||||
.RG_MACEA3 = 0x328,
|
||||
.RG_MACEA4 = 0x329,
|
||||
.RG_MACEA5 = 0x32A,
|
||||
.RG_MACEA6 = 0x32B,
|
||||
.RG_MACEA7 = 0x32C,
|
||||
.RG_MACPID0F0 = 0x32D,
|
||||
.RG_MACPID1F0 = 0x32E,
|
||||
.RG_MACSHA0F0 = 0x32F,
|
||||
.RG_MACSHA1F0 = 0x330,
|
||||
.RG_MACPID0F1 = 0x331,
|
||||
.RG_MACPID1F1 = 0x332,
|
||||
.RG_MACSHA0F1 = 0x333,
|
||||
.RG_MACSHA1F1 = 0x334,
|
||||
.RG_MACPID0F2 = 0x335,
|
||||
.RG_MACPID1F2 = 0x336,
|
||||
.RG_MACSHA0F2 = 0x337,
|
||||
.RG_MACSHA1F2 = 0x338,
|
||||
.RG_MACPID0F3 = 0x339,
|
||||
.RG_MACPID1F3 = 0x33A,
|
||||
.RG_MACSHA0F3 = 0x33B,
|
||||
.RG_MACSHA1F3 = 0x33C,
|
||||
.RG_AMCS = 0x340,
|
||||
.RG_AMEDT = 0x341,
|
||||
.RG_AMAACKPD = 0x342,
|
||||
.RG_AMAACKTL = 0x343,
|
||||
.RG_AMAACKTH = 0x344,
|
||||
.RG_FSKC0 = 0x360,
|
||||
.RG_FSKC1 = 0x361,
|
||||
.RG_FSKC2 = 0x362,
|
||||
.RG_FSKC3 = 0x363,
|
||||
.RG_FSKC4 = 0x364,
|
||||
.RG_FSKPLL = 0x365,
|
||||
.RG_FSKSFD0L = 0x366,
|
||||
.RG_FSKSFD0H = 0x367,
|
||||
.RG_FSKSFD1L = 0x368,
|
||||
.RG_FSKSFD1H = 0x369,
|
||||
.RG_FSKPHRTX = 0x36A,
|
||||
.RG_FSKPHRRX = 0x36B,
|
||||
.RG_FSKRPC = 0x36C,
|
||||
.RG_FSKRPCONT = 0x36D,
|
||||
.RG_FSKRPCOFFT = 0x36E,
|
||||
.RG_FSKRRXFLL = 0x370,
|
||||
.RG_FSKRRXFLH = 0x371,
|
||||
.RG_FSKDM = 0x372,
|
||||
.RG_FSKPE0 = 0x373,
|
||||
.RG_FSKPE1 = 0x374,
|
||||
.RG_FSKPE2 = 0x375,
|
||||
.RG_PMUC = 0x380,
|
||||
.RG_PMUVAL = 0x381,
|
||||
.RG_PMUQF = 0x382,
|
||||
.RG_PMUI = 0x383,
|
||||
.RG_PMUQ = 0x384,
|
||||
.RG_CNTC = 0x390,
|
||||
.RG_CNT0 = 0x391,
|
||||
.RG_CNT1 = 0x392,
|
||||
.RG_CNT2 = 0x393,
|
||||
.RG_CNT3 = 0x394,
|
||||
};
|
||||
|
||||
static const struct at86rf215_BBC_regs BBC1_regs = {
|
||||
.RG_IRQS = 0x03,
|
||||
.RG_FBRXS = 0x3000,
|
||||
.RG_FBRXE = 0x37FE,
|
||||
.RG_FBTXS = 0x3800,
|
||||
.RG_FBTXE = 0x3FFE,
|
||||
.RG_IRQM = 0x400,
|
||||
.RG_PC = 0x401,
|
||||
.RG_PS = 0x402,
|
||||
.RG_RXFLL = 0x404,
|
||||
.RG_RXFLH = 0x405,
|
||||
.RG_TXFLL = 0x406,
|
||||
.RG_TXFLH = 0x407,
|
||||
.RG_FBLL = 0x408,
|
||||
.RG_FBLH = 0x409,
|
||||
.RG_FBLIL = 0x40A,
|
||||
.RG_FBLIH = 0x40B,
|
||||
.RG_OFDMPHRTX = 0x40C,
|
||||
.RG_OFDMPHRRX = 0x40D,
|
||||
.RG_OFDMC = 0x40E,
|
||||
.RG_OFDMSW = 0x40F,
|
||||
.RG_OQPSKC0 = 0x410,
|
||||
.RG_OQPSKC1 = 0x411,
|
||||
.RG_OQPSKC2 = 0x412,
|
||||
.RG_OQPSKC3 = 0x413,
|
||||
.RG_OQPSKPHRTX = 0x414,
|
||||
.RG_OQPSKPHRRX = 0x415,
|
||||
.RG_AFC0 = 0x420,
|
||||
.RG_AFC1 = 0x421,
|
||||
.RG_AFFTM = 0x422,
|
||||
.RG_AFFVM = 0x423,
|
||||
.RG_AFS = 0x424,
|
||||
.RG_MACEA0 = 0x425,
|
||||
.RG_MACEA1 = 0x426,
|
||||
.RG_MACEA2 = 0x427,
|
||||
.RG_MACEA3 = 0x428,
|
||||
.RG_MACEA4 = 0x429,
|
||||
.RG_MACEA5 = 0x42A,
|
||||
.RG_MACEA6 = 0x42B,
|
||||
.RG_MACEA7 = 0x42C,
|
||||
.RG_MACPID0F0 = 0x42D,
|
||||
.RG_MACPID1F0 = 0x42E,
|
||||
.RG_MACSHA0F0 = 0x42F,
|
||||
.RG_MACSHA1F0 = 0x430,
|
||||
.RG_MACPID0F1 = 0x431,
|
||||
.RG_MACPID1F1 = 0x432,
|
||||
.RG_MACSHA0F1 = 0x433,
|
||||
.RG_MACSHA1F1 = 0x434,
|
||||
.RG_MACPID0F2 = 0x435,
|
||||
.RG_MACPID1F2 = 0x436,
|
||||
.RG_MACSHA0F2 = 0x437,
|
||||
.RG_MACSHA1F2 = 0x438,
|
||||
.RG_MACPID0F3 = 0x439,
|
||||
.RG_MACPID1F3 = 0x43A,
|
||||
.RG_MACSHA0F3 = 0x43B,
|
||||
.RG_MACSHA1F3 = 0x43C,
|
||||
.RG_AMCS = 0x440,
|
||||
.RG_AMEDT = 0x441,
|
||||
.RG_AMAACKPD = 0x442,
|
||||
.RG_AMAACKTL = 0x443,
|
||||
.RG_AMAACKTH = 0x444,
|
||||
.RG_FSKC0 = 0x460,
|
||||
.RG_FSKC1 = 0x461,
|
||||
.RG_FSKC2 = 0x462,
|
||||
.RG_FSKC3 = 0x463,
|
||||
.RG_FSKC4 = 0x464,
|
||||
.RG_FSKPLL = 0x465,
|
||||
.RG_FSKSFD0L = 0x466,
|
||||
.RG_FSKSFD0H = 0x467,
|
||||
.RG_FSKSFD1L = 0x468,
|
||||
.RG_FSKSFD1H = 0x469,
|
||||
.RG_FSKPHRTX = 0x46A,
|
||||
.RG_FSKPHRRX = 0x46B,
|
||||
.RG_FSKRPC = 0x46C,
|
||||
.RG_FSKRPCONT = 0x46D,
|
||||
.RG_FSKRPCOFFT = 0x46E,
|
||||
.RG_FSKRRXFLL = 0x470,
|
||||
.RG_FSKRRXFLH = 0x471,
|
||||
.RG_FSKDM = 0x472,
|
||||
.RG_FSKPE0 = 0x473,
|
||||
.RG_FSKPE1 = 0x474,
|
||||
.RG_FSKPE2 = 0x475,
|
||||
.RG_PMUC = 0x480,
|
||||
.RG_PMUVAL = 0x481,
|
||||
.RG_PMUQF = 0x482,
|
||||
.RG_PMUI = 0x483,
|
||||
.RG_PMUQ = 0x484,
|
||||
.RG_CNTC = 0x490,
|
||||
.RG_CNT0 = 0x491,
|
||||
.RG_CNT1 = 0x492,
|
||||
.RG_CNT2 = 0x493,
|
||||
.RG_CNT3 = 0x494,
|
||||
};
|
||||
|
||||
// BBCn_PC – PHY Control
|
||||
// This register configures the baseband PHY.
|
||||
void at86rf215_bb_set_phy_control (at86rf215_st *dev, at86rf215_rf_channel_en ch, at86rf215_bb_phy_control_st* pc)
|
||||
{
|
||||
|
||||
}
|
|
@ -1,212 +0,0 @@
|
|||
#ifndef __AT86RF215_BASEBAND_H__
|
||||
#define __AT86RF215_BASEBAND_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct at86rf215_BBC_regs
|
||||
{
|
||||
uint16_t RG_IRQS;
|
||||
uint16_t RG_FBRXS;
|
||||
uint16_t RG_FBRXE;
|
||||
uint16_t RG_FBTXS;
|
||||
uint16_t RG_FBTXE;
|
||||
uint16_t RG_IRQM;
|
||||
uint16_t RG_PC;
|
||||
uint16_t RG_PS;
|
||||
uint16_t RG_RXFLL;
|
||||
uint16_t RG_RXFLH;
|
||||
uint16_t RG_TXFLL;
|
||||
uint16_t RG_TXFLH;
|
||||
uint16_t RG_FBLL;
|
||||
uint16_t RG_FBLH;
|
||||
uint16_t RG_FBLIL;
|
||||
uint16_t RG_FBLIH;
|
||||
uint16_t RG_OFDMPHRTX;
|
||||
uint16_t RG_OFDMPHRRX;
|
||||
uint16_t RG_OFDMC;
|
||||
uint16_t RG_OFDMSW;
|
||||
uint16_t RG_OQPSKC0;
|
||||
uint16_t RG_OQPSKC1;
|
||||
uint16_t RG_OQPSKC2;
|
||||
uint16_t RG_OQPSKC3;
|
||||
uint16_t RG_OQPSKPHRTX;
|
||||
uint16_t RG_OQPSKPHRRX;
|
||||
uint16_t RG_AFC0;
|
||||
uint16_t RG_AFC1;
|
||||
uint16_t RG_AFFTM;
|
||||
uint16_t RG_AFFVM;
|
||||
uint16_t RG_AFS;
|
||||
uint16_t RG_MACEA0;
|
||||
uint16_t RG_MACEA1;
|
||||
uint16_t RG_MACEA2;
|
||||
uint16_t RG_MACEA3;
|
||||
uint16_t RG_MACEA4;
|
||||
uint16_t RG_MACEA5;
|
||||
uint16_t RG_MACEA6;
|
||||
uint16_t RG_MACEA7;
|
||||
uint16_t RG_MACPID0F0;
|
||||
uint16_t RG_MACPID1F0;
|
||||
uint16_t RG_MACSHA0F0;
|
||||
uint16_t RG_MACSHA1F0;
|
||||
uint16_t RG_MACPID0F1;
|
||||
uint16_t RG_MACPID1F1;
|
||||
uint16_t RG_MACSHA0F1;
|
||||
uint16_t RG_MACSHA1F1;
|
||||
uint16_t RG_MACPID0F2;
|
||||
uint16_t RG_MACPID1F2;
|
||||
uint16_t RG_MACSHA0F2;
|
||||
uint16_t RG_MACSHA1F2;
|
||||
uint16_t RG_MACPID0F3;
|
||||
uint16_t RG_MACPID1F3;
|
||||
uint16_t RG_MACSHA0F3;
|
||||
uint16_t RG_MACSHA1F3;
|
||||
uint16_t RG_AMCS;
|
||||
uint16_t RG_AMEDT;
|
||||
uint16_t RG_AMAACKPD;
|
||||
uint16_t RG_AMAACKTL;
|
||||
uint16_t RG_AMAACKTH;
|
||||
uint16_t RG_FSKC0;
|
||||
uint16_t RG_FSKC1;
|
||||
uint16_t RG_FSKC2;
|
||||
uint16_t RG_FSKC3;
|
||||
uint16_t RG_FSKC4;
|
||||
uint16_t RG_FSKPLL;
|
||||
uint16_t RG_FSKSFD0L;
|
||||
uint16_t RG_FSKSFD0H;
|
||||
uint16_t RG_FSKSFD1L;
|
||||
uint16_t RG_FSKSFD1H;
|
||||
uint16_t RG_FSKPHRTX;
|
||||
uint16_t RG_FSKPHRRX;
|
||||
uint16_t RG_FSKRPC;
|
||||
uint16_t RG_FSKRPCONT;
|
||||
uint16_t RG_FSKRPCOFFT;
|
||||
uint16_t RG_FSKRRXFLL;
|
||||
uint16_t RG_FSKRRXFLH;
|
||||
uint16_t RG_FSKDM;
|
||||
uint16_t RG_FSKPE0;
|
||||
uint16_t RG_FSKPE1;
|
||||
uint16_t RG_FSKPE2;
|
||||
uint16_t RG_PMUC;
|
||||
uint16_t RG_PMUVAL;
|
||||
uint16_t RG_PMUQF;
|
||||
uint16_t RG_PMUI;
|
||||
uint16_t RG_PMUQ;
|
||||
uint16_t RG_CNTC;
|
||||
uint16_t RG_CNT0;
|
||||
uint16_t RG_CNT1;
|
||||
uint16_t RG_CNT2;
|
||||
uint16_t RG_CNT3;
|
||||
};
|
||||
|
||||
/** @} */
|
||||
|
||||
/** BPSK, rate ½, 4 x frequency repetition */
|
||||
#define BB_MCS_BPSK_REP4 0
|
||||
/** BPSK, rate ½, 2 x frequency repetition */
|
||||
#define BB_MCS_BPSK_REP2 1
|
||||
/** QPSK, rate ½, 2 x frequency repetition */
|
||||
#define BB_MCS_QPSK_REP2 2
|
||||
/** QPSK, rate ½ */
|
||||
#define BB_MCS_QPSK_1BY2 3
|
||||
/** QPSK, rate ¾ */
|
||||
#define BB_MCS_QPSK_3BY4 4
|
||||
/** 16-QAM, rate ½ */
|
||||
#define BB_MCS_16QAM_1BY2 5
|
||||
/** 16-QAM, rate ¾ */
|
||||
#define BB_MCS_16QAM_3BY4 6
|
||||
|
||||
/** receive only MR-O-QPSK */
|
||||
#define RXM_MR_OQPSK 0x0
|
||||
/** receive only legacy O-QPSK */
|
||||
#define RXM_LEGACY_OQPSK 0x1
|
||||
/** receive both legacy & MR-O-QPSK */
|
||||
#define RXM_BOTH_OQPSK 0x2
|
||||
/** receive nothing */
|
||||
#define RXM_DISABLE 0x3
|
||||
|
||||
/** Modulation Order 2-FSK */
|
||||
#define FSK_MORD_2SFK (0 << FSKC0_MORD_SHIFT)
|
||||
/** Modulation Order 4-FSK */
|
||||
#define FSK_MORD_4SFK (1 << FSKC0_MORD_SHIFT)
|
||||
|
||||
/**
|
||||
* FSK modulation index
|
||||
* @{
|
||||
*/
|
||||
#define FSK_MIDX_3_BY_8 (0 << FSKC0_MIDX_SHIFT)
|
||||
#define FSK_MIDX_4_BY_8 (1 << FSKC0_MIDX_SHIFT)
|
||||
#define FSK_MIDX_6_BY_8 (2 << FSKC0_MIDX_SHIFT)
|
||||
#define FSK_MIDX_8_BY_8 (3 << FSKC0_MIDX_SHIFT)
|
||||
#define FSK_MIDX_10_BY_8 (4 << FSKC0_MIDX_SHIFT)
|
||||
#define FSK_MIDX_12_BY_8 (5 << FSKC0_MIDX_SHIFT)
|
||||
#define FSK_MIDX_14_BY_8 (6 << FSKC0_MIDX_SHIFT)
|
||||
#define FSK_MIDX_16_BY_8 (7 << FSKC0_MIDX_SHIFT)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* FSK modulation index scale
|
||||
* @{
|
||||
*/
|
||||
#define FSK_MIDXS_SCALE_7_BY_8 (0 << FSKC0_MIDXS_SHIFT)
|
||||
#define FSK_MIDXS_SCALE_8_BY_8 (1 << FSKC0_MIDXS_SHIFT)
|
||||
#define FSK_MIDXS_SCALE_9_BY_8 (2 << FSKC0_MIDXS_SHIFT)
|
||||
#define FSK_MIDXS_SCALE_10_BY_8 (3 << FSKC0_MIDXS_SHIFT)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* FSK bandwidth time product
|
||||
* @{
|
||||
*/
|
||||
#define FSK_BT_05 (0 << FSKC0_BT_SHIFT)
|
||||
#define FSK_BT_10 (1 << FSKC0_BT_SHIFT)
|
||||
#define FSK_BT_15 (2 << FSKC0_BT_SHIFT)
|
||||
#define FSK_BT_20 (3 << FSKC0_BT_SHIFT)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* FSK symbol rate (kHz)
|
||||
* @{
|
||||
*/
|
||||
#define FSK_SRATE_50K 0x0
|
||||
#define FSK_SRATE_100K 0x1
|
||||
#define FSK_SRATE_150K 0x2
|
||||
#define FSK_SRATE_200K 0x3
|
||||
#define FSK_SRATE_300K 0x4
|
||||
#define FSK_SRATE_400K 0x5
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* FSK channel spacing (kHz)
|
||||
* @{
|
||||
*/
|
||||
#define FSK_CHANNEL_SPACING_200K 0x0
|
||||
#define FSK_CHANNEL_SPACING_400K 0x1
|
||||
/** @} */
|
||||
|
||||
/** Lower values increase the SFD detector sensitivity.
|
||||
Higher values increase the SFD selectivity.
|
||||
The default value 8 is recommended for simultaneous sensing
|
||||
of the SFD pairs according to IEEE 802.15.4g. */
|
||||
#define FSKC3_SFDT(n) (((n) << FSKC3_SFDT_SHIFT) & FSKC3_SFDT_MASK)
|
||||
|
||||
/** Lower values increase the preamble detector sensitivity. */
|
||||
#define FSKC3_PDT(n) (((n) << FSKC3_PDT_SHIFT) & FSKC3_PDT_MASK)
|
||||
|
||||
/** @} */
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
|
||||
} at86rf215_bb_phy_control_st;
|
||||
|
||||
void at86rf215_bb_set_phy_control (at86rf215_st *dev, at86rf215_rf_channel_en ch, at86rf215_bb_phy_control_st* pc);
|
||||
void at86rf215_bb_get_phy_control (at86rf215_st *dev, at86rf215_rf_channel_en ch, at86rf215_bb_phy_control_st* pc);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __AT86RF215_BASEBAND_H__
|
|
@ -1,165 +0,0 @@
|
|||
#ifndef __AT86RF215_COMMON_H__
|
||||
#define __AT86RF215_COMMON_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include "io_utils/io_utils.h"
|
||||
#include "io_utils/io_utils_spi.h"
|
||||
#include "at86rf215_regs.h"
|
||||
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
typedef enum
|
||||
{
|
||||
at86rf215_rf_channel_900mhz = 0,
|
||||
at86rf215_rf_channel_2400mhz = 1,
|
||||
} at86rf215_rf_channel_en;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t wake_up_por:1;
|
||||
uint8_t trx_ready:1;
|
||||
uint8_t energy_detection_complete:1;
|
||||
uint8_t battery_low:1;
|
||||
uint8_t trx_error:1;
|
||||
uint8_t IQ_if_sync_fail:1;
|
||||
uint8_t res :2;
|
||||
} at86rf215_radio_irq_st;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t frame_rx_started:1;
|
||||
uint8_t frame_rx_complete:1;
|
||||
uint8_t frame_rx_address_match:1;
|
||||
uint8_t frame_rx_match_extended:1;
|
||||
uint8_t frame_tx_complete:1;
|
||||
uint8_t agc_hold:1;
|
||||
uint8_t agc_release :1;
|
||||
uint8_t frame_buffer_level :1;
|
||||
} at86rf215_baseband_irq_st;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
at86rf215_radio_irq_st radio09;
|
||||
at86rf215_radio_irq_st radio24;
|
||||
at86rf215_baseband_irq_st bb0;
|
||||
at86rf215_baseband_irq_st bb1;
|
||||
} at86rf215_irq_st;
|
||||
#pragma pack()
|
||||
|
||||
typedef enum
|
||||
{
|
||||
at86rf215_iq_drive_current_1ma = 0,
|
||||
at86rf215_iq_drive_current_2ma = 1,
|
||||
at86rf215_iq_drive_current_3ma = 2,
|
||||
at86rf215_iq_drive_current_4ma = 3,
|
||||
} at86rf215_iq_drive_current_en;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
at86rf215_iq_common_mode_v_150mv = 0,
|
||||
at86rf215_iq_common_mode_v_200mv = 1,
|
||||
at86rf215_iq_common_mode_v_250mv = 2,
|
||||
at86rf215_iq_common_mode_v_300mv = 3,
|
||||
at86rf215_iq_common_mode_v_ieee1596_1v2 = 4,
|
||||
} at86rf215_iq_common_mode_v_en;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
at86rf215_baseband_mode = 0,
|
||||
at86rf215_iq_if_mode = 1,
|
||||
} at86rf215_baseband_iq_mode_en;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
at86rf215_iq_clock_data_skew_1_906ns = 0,
|
||||
at86rf215_iq_clock_data_skew_2_906ns = 1,
|
||||
at86rf215_iq_clock_data_skew_3_906ns = 2,
|
||||
at86rf215_iq_clock_data_skew_4_906ns = 3,
|
||||
} at86rf215_iq_clock_data_skew_en;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t loopback_enable;
|
||||
at86rf215_iq_drive_current_en drv_strength;
|
||||
at86rf215_iq_common_mode_v_en common_mode_voltage;
|
||||
uint8_t tx_control_with_iq_if;
|
||||
at86rf215_baseband_iq_mode_en radio09_mode;
|
||||
at86rf215_baseband_iq_mode_en radio24_mode;
|
||||
at86rf215_iq_clock_data_skew_en clock_skew;
|
||||
|
||||
// status
|
||||
uint8_t synchronization_failed;
|
||||
uint8_t in_failsafe_mode;
|
||||
uint8_t synchronized_incoming_iq;
|
||||
} at86rf215_iq_interface_config_st;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int low_ch_i;
|
||||
int low_ch_q;
|
||||
int hi_ch_i;
|
||||
int hi_ch_q;
|
||||
} at86rf215_cal_results_st;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
pthread_mutex_t ready_mutex;
|
||||
pthread_cond_t ready_cond;
|
||||
int ready;
|
||||
} event_st;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
event_st lo_trx_ready_event;
|
||||
event_st lo_energy_measure_event;
|
||||
event_st hi_trx_ready_event;
|
||||
event_st hi_energy_measure_event;
|
||||
} at86rf215_events_st;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// pinout
|
||||
int reset_pin;
|
||||
int irq_pin;
|
||||
int cs_pin;
|
||||
|
||||
// spi device
|
||||
int spi_dev;
|
||||
int spi_channel;
|
||||
|
||||
// internal controls
|
||||
io_utils_spi_st* io_spi;
|
||||
int io_spi_handle;
|
||||
int initialized;
|
||||
at86rf215_cal_results_st cal;
|
||||
bool override_cal;
|
||||
at86rf215_events_st events;
|
||||
int num_interrupts;
|
||||
} at86rf215_st;
|
||||
|
||||
|
||||
int at86rf215_write_buffer(at86rf215_st* dev, uint16_t addr, uint8_t *buffer, uint8_t size );
|
||||
int at86rf215_read_buffer(at86rf215_st* dev, uint16_t addr, uint8_t *buffer, uint8_t size);
|
||||
int at86rf215_write_byte(at86rf215_st* dev, uint16_t addr, uint8_t val );
|
||||
int at86rf215_read_byte(at86rf215_st* dev, uint16_t addr);
|
||||
void at86rf215_interrupt_handler (int event, int level, uint32_t tick, void *data);
|
||||
int at86rf215_write_fifo(at86rf215_st* dev, uint8_t *buffer, uint8_t size );
|
||||
int at86rf215_read_fifo(at86rf215_st* dev, uint8_t *buffer, uint8_t size );
|
||||
void at86rf215_get_irqs(at86rf215_st* dev, at86rf215_irq_st* irq, int verbose);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __AT86RF215_COMMON_H__
|
|
@ -1,157 +0,0 @@
|
|||
#ifndef ZF_LOG_LEVEL
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#endif
|
||||
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "AT86RF215_Events"
|
||||
|
||||
#include <stdio.h>
|
||||
#include "zf_log/zf_log.h"
|
||||
#include "at86rf215_common.h"
|
||||
#include <pthread.h>
|
||||
|
||||
|
||||
void event_node_init(event_st* ev)
|
||||
{
|
||||
pthread_cond_init(&ev->ready_cond, NULL);
|
||||
pthread_mutex_init(&ev->ready_mutex, NULL);
|
||||
}
|
||||
|
||||
void event_node_close(event_st* ev)
|
||||
{
|
||||
pthread_cond_destroy(&ev->ready_cond);
|
||||
pthread_mutex_destroy(&ev->ready_mutex);
|
||||
}
|
||||
|
||||
void event_node_wait_ready(event_st* ev)
|
||||
{
|
||||
pthread_mutex_lock(&ev->ready_mutex);
|
||||
while (!ev->ready)
|
||||
{
|
||||
pthread_cond_wait(&ev->ready_cond, &ev->ready_mutex);
|
||||
}
|
||||
ev->ready = 0;
|
||||
pthread_mutex_unlock(&ev->ready_mutex);
|
||||
}
|
||||
|
||||
void event_node_signal_ready(event_st* ev, int ready)
|
||||
{
|
||||
pthread_mutex_lock(&ev->ready_mutex);
|
||||
ev->ready = ready;
|
||||
pthread_cond_signal(&ev->ready_cond);
|
||||
pthread_mutex_unlock(&ev->ready_mutex);
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
static void at86rf215_radio_event_handler (at86rf215_st* dev,
|
||||
at86rf215_rf_channel_en ch,
|
||||
at86rf215_radio_irq_st *events)
|
||||
{
|
||||
char channel_st[3];
|
||||
if (ch == at86rf215_rf_channel_900mhz) strcpy(channel_st, "09");
|
||||
else strcpy(channel_st, "24");
|
||||
|
||||
if (events->wake_up_por)
|
||||
{
|
||||
ZF_LOGD("INT @ RADIO%s: Woke up", channel_st);
|
||||
}
|
||||
|
||||
if (events->trx_ready)
|
||||
{
|
||||
ZF_LOGD("INT @ RADIO%s: Transceiver ready", channel_st);
|
||||
if (ch == at86rf215_rf_channel_900mhz) event_node_signal_ready(&dev->events.lo_trx_ready_event, 1);
|
||||
else if (ch == at86rf215_rf_channel_2400mhz) event_node_signal_ready(&dev->events.hi_trx_ready_event, 1);
|
||||
}
|
||||
|
||||
if (events->energy_detection_complete)
|
||||
{
|
||||
ZF_LOGD("INT @ RADIO%s: Energy detection complete", channel_st);
|
||||
if (ch == at86rf215_rf_channel_900mhz) event_node_signal_ready(&dev->events.lo_energy_measure_event, 1);
|
||||
else if (ch == at86rf215_rf_channel_2400mhz) event_node_signal_ready(&dev->events.hi_energy_measure_event, 1);
|
||||
}
|
||||
|
||||
if (events->battery_low)
|
||||
{
|
||||
ZF_LOGD("INT @ RADIO%s: Battery low", channel_st);
|
||||
}
|
||||
|
||||
if (events->trx_error)
|
||||
{
|
||||
ZF_LOGD("INT @ RADIO%s: Transceiver error", channel_st);
|
||||
}
|
||||
|
||||
if (events->IQ_if_sync_fail)
|
||||
{
|
||||
ZF_LOGD("INT @ RADIO%s: I/Q interface sync failed", channel_st);
|
||||
}
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
static void at86rf215_baseband_event_handler (at86rf215_st* dev,
|
||||
at86rf215_rf_channel_en ch,
|
||||
at86rf215_baseband_irq_st *events)
|
||||
{
|
||||
char channel_st[3];
|
||||
if (ch == at86rf215_rf_channel_900mhz) strcpy(channel_st, "09");
|
||||
else strcpy(channel_st, "24");
|
||||
|
||||
|
||||
if (events->frame_rx_started)
|
||||
{
|
||||
ZF_LOGD("INT @ BB%s: Frame reception started", channel_st);
|
||||
}
|
||||
|
||||
if (events->frame_rx_complete)
|
||||
{
|
||||
ZF_LOGD("INT @ BB%s: Frame reception complete", channel_st);
|
||||
}
|
||||
|
||||
if (events->frame_rx_address_match)
|
||||
{
|
||||
ZF_LOGD("INT @ BB%s: Frame address matched", channel_st);
|
||||
}
|
||||
|
||||
if (events->frame_rx_match_extended)
|
||||
{
|
||||
ZF_LOGD("INT @ BB%s: Frame extended address matched", channel_st);
|
||||
}
|
||||
|
||||
if (events->frame_tx_complete)
|
||||
{
|
||||
ZF_LOGD("INT @ BB%s: Frame transmission complete", channel_st);
|
||||
}
|
||||
|
||||
if (events->agc_hold)
|
||||
{
|
||||
ZF_LOGD("INT @ BB%s: AGC hold", channel_st);
|
||||
}
|
||||
|
||||
if (events->agc_release)
|
||||
{
|
||||
ZF_LOGD("INT @ BB%s: AGC released", channel_st);
|
||||
}
|
||||
|
||||
if (events->frame_buffer_level)
|
||||
{
|
||||
ZF_LOGD("INT @ BB%s: Frame buffer level", channel_st);
|
||||
}
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
void at86rf215_interrupt_handler (int event, int level, uint32_t tick, void *data)
|
||||
{
|
||||
int i;
|
||||
at86rf215_st* dev = (at86rf215_st*)data;
|
||||
at86rf215_irq_st irq = {0};
|
||||
|
||||
if (level == 0) return;
|
||||
// first read the irqs
|
||||
at86rf215_get_irqs(dev, &irq, 0);
|
||||
uint8_t *tmp = (uint8_t *)&irq;
|
||||
dev->num_interrupts ++;
|
||||
|
||||
if (tmp[0] != 0) at86rf215_radio_event_handler (dev, at86rf215_rf_channel_900mhz, &irq.radio09);
|
||||
if (tmp[1] != 0) at86rf215_radio_event_handler (dev, at86rf215_rf_channel_2400mhz, &irq.radio24);
|
||||
if (tmp[2] != 0) at86rf215_baseband_event_handler (dev, at86rf215_rf_channel_900mhz, &irq.bb0);
|
||||
if (tmp[3] != 0) at86rf215_baseband_event_handler (dev, at86rf215_rf_channel_2400mhz, &irq.bb1);
|
||||
}
|
|
@ -1,705 +0,0 @@
|
|||
#ifndef ZF_LOG_LEVEL
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#endif
|
||||
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "AT86RF215_Radio"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include "zf_log/zf_log.h"
|
||||
#include "io_utils/io_utils.h"
|
||||
#include "io_utils/io_utils_spi.h"
|
||||
#include "at86rf215_radio.h"
|
||||
#include "at86rf215_regs.h"
|
||||
|
||||
static const struct at86rf215_radio_regs RF09_regs = {
|
||||
.RG_IRQS = 0x00,
|
||||
.RG_IRQM = 0x100,
|
||||
.RG_AUXS = 0x101,
|
||||
.RG_STATE = 0x102,
|
||||
.RG_CMD = 0x103,
|
||||
.RG_CS = 0x104,
|
||||
.RG_CCF0L = 0x105,
|
||||
.RG_CCF0H = 0x106,
|
||||
.RG_CNL = 0x107,
|
||||
.RG_CNM = 0x108,
|
||||
.RG_RXBWC = 0x109,
|
||||
.RG_RXDFE = 0x10A,
|
||||
.RG_AGCC = 0x10B,
|
||||
.RG_AGCS = 0x10C,
|
||||
.RG_RSSI = 0x10D,
|
||||
.RG_EDC = 0x10E,
|
||||
.RG_EDD = 0x10F,
|
||||
.RG_EDV = 0x110,
|
||||
.RG_RNDV = 0x111,
|
||||
.RG_TXCUTC = 0x112,
|
||||
.RG_TXDFE = 0x113,
|
||||
.RG_PAC = 0x114,
|
||||
.RG_PADFE = 0x116,
|
||||
.RG_PLL = 0x121,
|
||||
.RG_PLLCF = 0x122,
|
||||
.RG_TXCI = 0x125,
|
||||
.RG_TXCQ = 0x126,
|
||||
.RG_TXDACI = 0x127,
|
||||
.RG_TXDACQ = 0x128,
|
||||
};
|
||||
|
||||
static const struct at86rf215_radio_regs RF24_regs = {
|
||||
.RG_IRQS = 0x01,
|
||||
.RG_IRQM = 0x200,
|
||||
.RG_AUXS = 0x201,
|
||||
.RG_STATE = 0x202,
|
||||
.RG_CMD = 0x203,
|
||||
.RG_CS = 0x204,
|
||||
.RG_CCF0L = 0x205,
|
||||
.RG_CCF0H = 0x206,
|
||||
.RG_CNL = 0x207,
|
||||
.RG_CNM = 0x208,
|
||||
.RG_RXBWC = 0x209,
|
||||
.RG_RXDFE = 0x20A,
|
||||
.RG_AGCC = 0x20B,
|
||||
.RG_AGCS = 0x20C,
|
||||
.RG_RSSI = 0x20D,
|
||||
.RG_EDC = 0x20E,
|
||||
.RG_EDD = 0x20F,
|
||||
.RG_EDV = 0x210,
|
||||
.RG_RNDV = 0x211,
|
||||
.RG_TXCUTC = 0x212,
|
||||
.RG_TXDFE = 0x213,
|
||||
.RG_PAC = 0x214,
|
||||
.RG_PADFE = 0x216,
|
||||
.RG_PLL = 0x221,
|
||||
.RG_PLLCF = 0x222,
|
||||
.RG_TXCI = 0x225,
|
||||
.RG_TXCQ = 0x226,
|
||||
.RG_TXDACI = 0x227,
|
||||
.RG_TXDACQ = 0x228,
|
||||
};
|
||||
|
||||
#define AT86RF215_REG_ADDR(c,r) \
|
||||
(((c)==at86rf215_rf_channel_900mhz)?(RF09_regs.RG_##r):(RF24_regs.RG_##r))
|
||||
|
||||
|
||||
//==================================================================================
|
||||
void at86rf215_radio_setup_interrupt_mask(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
at86rf215_radio_irq_st* mask)
|
||||
{
|
||||
uint16_t reg_address = AT86RF215_REG_ADDR(ch, IRQM);
|
||||
at86rf215_write_byte(dev, reg_address, *((uint8_t*)mask));
|
||||
}
|
||||
|
||||
//==================================================================================
|
||||
void at86rf215_radio_setup_external_settings(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
at86rf215_radio_external_ctrl_st* cfg)
|
||||
{
|
||||
// "RG_AUXS" RFn_AUXS – Transceiver Auxiliary Settings
|
||||
uint16_t reg_address_aux = AT86RF215_REG_ADDR(ch, AUXS);
|
||||
uint16_t reg_address_pad = AT86RF215_REG_ADDR(ch, PADFE);
|
||||
|
||||
uint8_t aux = 0;
|
||||
aux |= (cfg->ext_lna_bypass_available&0x1) << 7;
|
||||
aux |= (cfg->agc_backoff&0x3) << 5;
|
||||
aux |= (cfg->analog_voltage_external&0x1) << 4;
|
||||
aux |= (cfg->analog_voltage_enable_in_off&0x1) << 3;
|
||||
aux |= (cfg->int_power_amplifier_voltage&0x3) << 0;
|
||||
|
||||
at86rf215_write_byte(dev, reg_address_aux, aux);
|
||||
|
||||
uint8_t pad = (cfg->fe_pad_configuration&0x3) << 6;
|
||||
at86rf215_write_byte(dev, reg_address_pad, pad);
|
||||
}
|
||||
|
||||
//==================================================================================
|
||||
void at86rf215_radio_get_external_settings(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
at86rf215_radio_external_ctrl_st* cfg)
|
||||
{
|
||||
uint16_t reg_address_aux = AT86RF215_REG_ADDR(ch, AUXS);
|
||||
uint16_t reg_address_pad = AT86RF215_REG_ADDR(ch, PADFE);
|
||||
|
||||
uint8_t aux = at86rf215_read_byte(dev, reg_address_aux);
|
||||
uint8_t pad = at86rf215_read_byte(dev, reg_address_pad);
|
||||
|
||||
cfg->fe_pad_configuration = pad>>6;
|
||||
cfg->ext_lna_bypass_available = (aux>>7) & 0x1;
|
||||
cfg->agc_backoff = (aux>>5) & 0x3;
|
||||
cfg->analog_voltage_external = (aux>>4) & 0x1;
|
||||
cfg->analog_voltage_enable_in_off = (aux>>3) & 0x1;
|
||||
cfg->analog_voltage_status = (aux>>2) & 0x1;
|
||||
cfg->int_power_amplifier_voltage = (aux>>0) & 0x2;
|
||||
}
|
||||
|
||||
//==================================================================================
|
||||
at86rf215_radio_state_cmd_en at86rf215_radio_get_state(at86rf215_st* dev, at86rf215_rf_channel_en ch)
|
||||
{
|
||||
// "RG_STATE" RFn_STATE – Transceiver State
|
||||
uint16_t reg_address = AT86RF215_REG_ADDR(ch, STATE);
|
||||
uint8_t state = at86rf215_read_byte(dev, reg_address);
|
||||
return state & 0x7;
|
||||
}
|
||||
|
||||
//==================================================================================
|
||||
void at86rf215_radio_set_state(at86rf215_st* dev, at86rf215_rf_channel_en ch, at86rf215_radio_state_cmd_en cmd)
|
||||
{
|
||||
// "RG_CMD" RFn_CMD – Transceiver Command
|
||||
|
||||
uint16_t reg_address = AT86RF215_REG_ADDR(ch, CMD);
|
||||
at86rf215_write_byte(dev, reg_address, cmd & 0x7);
|
||||
|
||||
/*Errata #6: State Machine Command RFn_CMD=TRXOFF may not be succeeded
|
||||
Description: If the current state is different from SLEEP, the execution of the command TRXOFF may fail.
|
||||
Software workaround: Check state by reading register RFn_STATE Repeat the command RFn_CMD=TRXOFF
|
||||
if the target state was not reached.
|
||||
*/
|
||||
if (cmd == at86rf215_radio_state_cmd_trx_off)
|
||||
{
|
||||
int retries = 5;
|
||||
while (at86rf215_radio_get_state(dev, ch) != cmd && retries--)
|
||||
{
|
||||
io_utils_usleep(100);
|
||||
at86rf215_write_byte(dev, reg_address, cmd & 0x7);
|
||||
}
|
||||
}
|
||||
if (cmd == at86rf215_radio_state_cmd_tx_prep || cmd == at86rf215_radio_state_cmd_tx || cmd == at86rf215_radio_state_cmd_rx)
|
||||
{
|
||||
//if (ch == at86rf215_rf_channel_900mhz) event_node_wait_ready(&dev->events.lo_trx_ready_event);
|
||||
//else if (ch == at86rf215_rf_channel_2400mhz) event_node_wait_ready(&dev->events.hi_trx_ready_event);
|
||||
|
||||
io_utils_usleep(1000);
|
||||
if (dev->override_cal)
|
||||
{
|
||||
int i = ch == at86rf215_rf_channel_900mhz ? dev->cal.low_ch_i : dev->cal.hi_ch_i;
|
||||
int q = ch == at86rf215_rf_channel_900mhz ? dev->cal.low_ch_q : dev->cal.hi_ch_q;
|
||||
at86rf215_radio_set_tx_iq_calibration(dev, ch, i, q);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static double _fine_freq_starts[] = {0, 377e6, 754e6, 2366e6, 2550e6};
|
||||
static double _fine_freq_pll_src[] = {0, 6.5e6, 13e6, 26e6};
|
||||
//static int _fine_freq_ccf_min[] = {0, 126030, 126030, 85700};
|
||||
//static int _fine_freq_ccf_max[] = {0, 1340967, 1340967, 296172};
|
||||
|
||||
//==================================================================================
|
||||
int at86rf215_radio_get_good_channel(double wanted_frequency_hz, at86rf215_radio_channel_mode_en *mode,
|
||||
at86rf215_rf_channel_en *ch)
|
||||
{
|
||||
if ( wanted_frequency_hz >= _fine_freq_starts[at86rf215_radio_channel_mode_fine_high]
|
||||
&& wanted_frequency_hz < _fine_freq_starts[at86rf215_radio_channel_mode_fine_high+1])
|
||||
{
|
||||
*ch = at86rf215_rf_channel_2400mhz;
|
||||
*mode = at86rf215_radio_channel_mode_fine_high;
|
||||
}
|
||||
else if ( wanted_frequency_hz >= _fine_freq_starts[at86rf215_radio_channel_mode_fine_mid]
|
||||
&& wanted_frequency_hz < _fine_freq_starts[at86rf215_radio_channel_mode_fine_high] )
|
||||
{
|
||||
*ch = at86rf215_rf_channel_900mhz;
|
||||
*mode = at86rf215_radio_channel_mode_fine_mid;
|
||||
}
|
||||
else if ( wanted_frequency_hz >= _fine_freq_starts[at86rf215_radio_channel_mode_fine_low]
|
||||
&& wanted_frequency_hz < _fine_freq_starts[at86rf215_radio_channel_mode_fine_mid] )
|
||||
{
|
||||
*ch = at86rf215_rf_channel_900mhz;
|
||||
*mode = at86rf215_radio_channel_mode_fine_low;
|
||||
}
|
||||
else
|
||||
{
|
||||
ZF_LOGE("the requested frequency %.1f Hz not supported", wanted_frequency_hz);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==================================================================================
|
||||
double at86rf215_radio_get_frequency( /*IN*/ at86rf215_radio_channel_mode_en mode,
|
||||
/*IN*/ int channel_spacing_25khz_res,
|
||||
/*IN*/ double wanted_frequency_hz,
|
||||
/*OUT*/ int *center_freq_25khz_res,
|
||||
/*OUT*/ int *channel_number)
|
||||
{
|
||||
// "RG_CS" RFn_CS – Channel Spacing
|
||||
/*
|
||||
The register configures the channel spacing with a resolution of 25kHz. The register CNM must be written to take over
|
||||
the CS value. The CNM register must always be written last.
|
||||
Example: A value of 0x08 needs to be set (8x25kHz) for a channel spacing of 200kHz.
|
||||
*/
|
||||
// "RG_CCF0L, RG_CCF0H" RFn_CCF0(L/H) – Channel Center Frequency F0 Low/High Byte
|
||||
/*
|
||||
The register configures the base value of the channel center frequency F0 (low / high byte) with a resolution of 25kHz.
|
||||
The register CNM must be written to latch the CCFOL/H value. The CNM register must always be written last.
|
||||
|
||||
VALUES: The reset value (0xf8) of the channel register results in 902.2MHz for the sub-1GHz transceiver (RF09)
|
||||
and 2402.2MHz for the 2.4GHz transceiver (RF24): CCF0=0x8cf8=36088; CS=200KHz, CN=0.
|
||||
|
||||
HOW:
|
||||
F_rf09 = CCF0*25khz
|
||||
F_rf24 = 1500000 + CCF0 * 25
|
||||
F_rf09(default) = 36088 * 25 = 902,200 kHz
|
||||
F_rf24(default) = 1500000 + 36088 * 25 = 2,402,200 kHz
|
||||
*/
|
||||
// "RG_CNL" RFn_CNL – Channel Number Low Byte
|
||||
// "RG_CNM" RFn_CNM – Channel Mode and Channel Number High Bit
|
||||
// in total the CNL/M contains 9 bits of channel and the LATCH (Mode)
|
||||
/*
|
||||
CNL - The register contains the transceiver channel number low byte CN[7:0]. The register CNM must be written to latch the
|
||||
CNL value. The CNM register must always be written last.
|
||||
|
||||
CNM - The register configures the channel mode and channel number bit 9 CN[8]. A write operation to this register applies the
|
||||
channel register CS, CCFOL, CCFOH and CNL values.
|
||||
|
||||
0x0 IEEE compliant channel scheme; f=(CCF0+CN*CS)*25kHz+f_offset ; (f_offset = 0Hz/1.5GHz)
|
||||
0x1 Fine resolution (389.5-510.0)MHz with 99.182Hz channel stepping
|
||||
0x2 Fine resolution (779-1020)MHz with 198.364Hz channel stepping
|
||||
0x3 Fine resolution (2400-2483.5)MHz with 396.728Hz channel stepping
|
||||
*/
|
||||
|
||||
|
||||
if (mode == at86rf215_radio_channel_mode_ieee)
|
||||
{
|
||||
float f_offset = (wanted_frequency_hz>1500e6)?1500e6:0.0f;
|
||||
*center_freq_25khz_res = (int)((wanted_frequency_hz - f_offset) / 25e3);
|
||||
*channel_number = 0;
|
||||
return f_offset + (*center_freq_25khz_res)*25e3;
|
||||
}
|
||||
|
||||
int nchannel = (int)((wanted_frequency_hz - _fine_freq_starts[mode]) * ((float)(1<<16)) / _fine_freq_pll_src[mode]);
|
||||
float actual_freq = (nchannel*_fine_freq_pll_src[mode]) / ((float)(1<<16)) + _fine_freq_starts[mode];
|
||||
|
||||
*center_freq_25khz_res = (nchannel >> 8) & 0xFFFF;
|
||||
*channel_number = (nchannel >> 0) & 0xFF;
|
||||
return actual_freq;
|
||||
}
|
||||
|
||||
//==================================================================================
|
||||
void at86rf215_radio_setup_channel(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
int channel_spacing_25khz_res,
|
||||
int center_freq_25khz_res,
|
||||
int channel_number,
|
||||
at86rf215_radio_channel_mode_en mode)
|
||||
{
|
||||
|
||||
uint16_t reg_address_spacing = AT86RF215_REG_ADDR(ch, CS);
|
||||
uint8_t buf[5] = {0};
|
||||
buf[0] = channel_spacing_25khz_res;
|
||||
buf[1] = /*LOW*/ center_freq_25khz_res & 0xFF;
|
||||
buf[2] = /*HIGH*/ (center_freq_25khz_res >> 8) & 0xFF;
|
||||
buf[3] = /*LOW*/ channel_number & 0xFF;
|
||||
buf[4] = /*HIGH + MODE*/ ((channel_number>>8)&0x01) | ((mode & 0x3)<<6);
|
||||
at86rf215_write_buffer(dev, reg_address_spacing, buf, 5);
|
||||
}
|
||||
|
||||
//==================================================================================
|
||||
void at86rf215_radio_set_rx_bandwidth_sampling(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
at86rf215_radio_set_rx_bw_samp_st* cfg)
|
||||
{
|
||||
// "RG_RXBWC" RFn_RXBWC – Receiver Filter Bandwidth Control
|
||||
/*
|
||||
The register configures the receiver filter settings.
|
||||
- Bit 5 – RXBWC.IFI: IF Inversion
|
||||
A value of one configures the receiver to implement the inverted-sign IF frequency.
|
||||
Use default setting for normal operation.
|
||||
- Bit 4 – RXBWC.IFS: IF Shift
|
||||
A value of one configures the receiver to shift the IF frequency by factor of 1.25.
|
||||
This is useful to place the image frequency according to channel scheme.
|
||||
- Bit 3:0 – RXBWC.BW: Receiver Bandwidth
|
||||
The sub-register controls the receiver filter bandwidth settings.
|
||||
*/
|
||||
// "RG_RXDFE" RFn_RXDFE – Receiver Digital Frontend
|
||||
/*
|
||||
The register configures the receiver digital frontend
|
||||
Bit 7:5 – RXDFE.RCUT: RX filter relative cut-off frequency
|
||||
Bit 3:0 – RXDFE.SR: RX Sample Rate
|
||||
*/
|
||||
|
||||
uint16_t reg_address_bw = AT86RF215_REG_ADDR(ch, RXBWC);
|
||||
|
||||
uint8_t buf[2] = {0};
|
||||
buf[0] |= (cfg->inverter_sign_if & 0x1)<<5;
|
||||
buf[0] |= (cfg->shift_if_freq & 0x1)<<4;
|
||||
buf[0] |= (cfg->bw & 0xF);
|
||||
buf[1] |= (cfg->fcut & 0x7) << 5;
|
||||
buf[1] |= (cfg->fs & 0xF);
|
||||
at86rf215_write_buffer(dev, reg_address_bw, buf, 2);
|
||||
}
|
||||
|
||||
//==================================================================================
|
||||
void at86rf215_radio_get_rx_bandwidth_sampling(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
at86rf215_radio_set_rx_bw_samp_st* cfg)
|
||||
{
|
||||
uint16_t reg_address_bw = AT86RF215_REG_ADDR(ch, RXBWC);
|
||||
uint8_t buf[2] = {0};
|
||||
at86rf215_read_buffer(dev, reg_address_bw, buf, 2);
|
||||
|
||||
cfg->inverter_sign_if = (buf[0]>>5) & 0x1;
|
||||
cfg->shift_if_freq = (buf[0]>>4) & 0x1;
|
||||
cfg->bw = (buf[0]>>0) & 0xF;
|
||||
cfg->fcut = (buf[1]>>5) & 0x7;
|
||||
cfg->fs = (buf[1]>>0) & 0xF;
|
||||
}
|
||||
|
||||
//==================================================================================
|
||||
void at86rf215_radio_setup_agc(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
at86rf215_radio_agc_ctrl_st *agc_ctrl)
|
||||
{
|
||||
// "RG_AGCC, RG_AGCS" RFn_AGCC – Receiver AGC Control 0 / RFn_AGCS – Receiver AGCG
|
||||
/*
|
||||
RFn_AGCC – Receiver AGC Control 0:
|
||||
The register controls the transceiver automatic gain control (AGC).
|
||||
Bit 6 – AGCC.AGCI: AGC Input (0 - filterred, 1 - unfiltered, faster operation)
|
||||
Bit 5:4 – AGCC.AVGS: AGC Average Time in Number of Samples
|
||||
0x0 8 samples
|
||||
0x1 16 samples
|
||||
0x2 32 samples
|
||||
0x3 64 samples
|
||||
Bit 3 – AGCC.RST: AGC Reset - resets the AGC and sets the maximum receiver gain.
|
||||
Bit 2 – AGCC.FRZS: AGC Freeze Status - A value of one indicates that the AGC is on hold.
|
||||
Bit 1 – AGCC.FRZC: AGC Freeze Control - A value of one forces the AGC to freeze to its current value.
|
||||
Bit 0 – AGCC.EN: AGC Enable - a value of zero allows a manual setting of the RX gain control by sub-register AGCS.GCW
|
||||
|
||||
RFn_AGCS – Receiver AGCG:
|
||||
The register controls the AGC and the receiver gain.
|
||||
Bit 7:5 – AGCS.TGT: AGC Target Level - sets the AGC target level relative to ADC full scale.
|
||||
0x0 Target=-21dB
|
||||
0x1 Target=-24dB
|
||||
0x2 Target=-27dB
|
||||
0x3 Target=-30dB
|
||||
0x4 Target=-33dB
|
||||
0x5 Target=-36dB
|
||||
0x6 Target=-39dB
|
||||
0x7 Target=-42dB
|
||||
Bit 4:0 – AGCS.GCW: RX Gain Control Word
|
||||
If AGCC_EN is set to 1, a read of bit AGCS.GCW indicates the current receiver gain setting. If AGCC_EN is set to 0, a
|
||||
write access to GCW manually sets the receiver gain. An integer value of 23 indicates the maximum receiver gain; each
|
||||
integer step changes the gain by 3dB.
|
||||
*/
|
||||
|
||||
uint16_t reg_address_agc = AT86RF215_REG_ADDR(ch, AGCC);
|
||||
uint8_t buf[2] = {0};
|
||||
|
||||
buf[0] |= (agc_ctrl->agc_measure_source_not_filtered & 0x1)<<6;
|
||||
buf[0] |= (agc_ctrl->avg & 0x3)<<4;
|
||||
buf[0] |= (agc_ctrl->reset_cmd & 0x1) << 3;
|
||||
buf[0] |= (agc_ctrl->freeze_cmd & 0x1) << 1;
|
||||
buf[0] |= (agc_ctrl->enable_cmd & 0x1);
|
||||
buf[1] |= (agc_ctrl->att & 0x7) << 5;
|
||||
buf[1] |= (agc_ctrl->gain_control_word & 0x1F);
|
||||
at86rf215_write_buffer(dev, reg_address_agc, buf, 2);
|
||||
}
|
||||
|
||||
//==================================================================================
|
||||
void at86rf215_radio_get_agc(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
at86rf215_radio_agc_ctrl_st *agc_ctrl)
|
||||
{
|
||||
uint16_t reg_address_agc = AT86RF215_REG_ADDR(ch, AGCC);
|
||||
uint8_t buf[2] = {0};
|
||||
|
||||
at86rf215_read_buffer(dev, reg_address_agc, buf, 2);
|
||||
|
||||
agc_ctrl->agc_measure_source_not_filtered = (buf[0] >> 6);
|
||||
agc_ctrl->avg = (buf[0] >> 4) & 0x1;
|
||||
agc_ctrl->reset_cmd = (buf[0] >> 3) & 0x3;
|
||||
agc_ctrl->freeze_status = (buf[0] >> 2) & 0x1;
|
||||
agc_ctrl->freeze_cmd = (buf[0] >> 1) & 0x1;
|
||||
agc_ctrl->enable_cmd = (buf[0] >> 0) & 0x1;
|
||||
agc_ctrl->att = (buf[1] >> 5) & 0x7;
|
||||
agc_ctrl->gain_control_word = (buf[1] >> 0) & 0x1F;
|
||||
}
|
||||
|
||||
//==================================================================================
|
||||
float at86rf215_radio_get_rssi_dbm(at86rf215_st* dev, at86rf215_rf_channel_en ch)
|
||||
{
|
||||
// "RG_RSSI" RFn_RSSI – Received Signal Strength Indicator
|
||||
/*
|
||||
Bit 7:0 – RSSI.RSSI: Received Signal Strength Indicator
|
||||
The value reflects the received signal power in dBm. A value of 127 indicates that the RSSI value is invalid. The register value presents a signed
|
||||
integer value (value range -127...+4) using twos complement representation.
|
||||
*/
|
||||
uint16_t reg_address = AT86RF215_REG_ADDR(ch, RSSI);
|
||||
uint8_t v = at86rf215_read_byte(dev, reg_address);
|
||||
int8_t *sv = (int8_t *)&v;
|
||||
return (float)(*sv);
|
||||
}
|
||||
|
||||
//==================================================================================
|
||||
void at86rf215_radio_setup_energy_detection(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
at86rf215_radio_energy_detection_st* ed)
|
||||
{
|
||||
/*
|
||||
"RG_EDC" "RG_EDD" "RG_EDV"
|
||||
RFn_EDC – Energy Detection Configuration
|
||||
Bit 1:0 – EDC.EDM: Energy Detection Mode (at86rf215_radio_energy_detection_mode_en)
|
||||
RFn_EDD – Receiver Energy Detection Averaging Duration
|
||||
Bit 7:2 – EDD.DF: Receiver energy detection duration factor - T[μs]=DF*DTB
|
||||
Bit 1:0 – EDD.DTB: Receiver energy detection average duration time basis (RSSI averaging basis)
|
||||
0x0 2μs
|
||||
0x1 8μs
|
||||
0x2 32μs
|
||||
0x3 128μs
|
||||
*/
|
||||
uint16_t reg_address_edc = AT86RF215_REG_ADDR(ch, EDC);
|
||||
uint8_t buf[2] = {0};
|
||||
|
||||
buf[0] |= (ed->mode & 0x3)<<0;
|
||||
|
||||
// find the closest usec number
|
||||
int df = ed->average_duration_us / 2;
|
||||
int dtb = 0;
|
||||
if (df > 63)
|
||||
{
|
||||
df = ed->average_duration_us / 8;
|
||||
dtb = 1;
|
||||
if (df > 63)
|
||||
{
|
||||
df = ed->average_duration_us / 32;
|
||||
dtb = 2;
|
||||
if (df > 63)
|
||||
{
|
||||
df = ed->average_duration_us / 128;
|
||||
dtb = 3;
|
||||
if (df > 63)
|
||||
{
|
||||
df = 63;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
buf[1] |= (df & 0x3F) << 2;
|
||||
buf[1] |= (dtb & 0x3);
|
||||
|
||||
at86rf215_write_buffer(dev, reg_address_edc, buf, 2);
|
||||
}
|
||||
|
||||
//==================================================================================
|
||||
void at86rf215_radio_get_energy_detection(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
at86rf215_radio_energy_detection_st* ed)
|
||||
{
|
||||
uint16_t reg_address_edc = AT86RF215_REG_ADDR(ch, EDC);
|
||||
uint8_t buf[3] = {0};
|
||||
|
||||
at86rf215_read_buffer(dev, reg_address_edc, buf, 3);
|
||||
|
||||
ed->mode = (buf[0]) & 0x3;
|
||||
buf[0] |= (ed->mode & 0x3)<<0;
|
||||
|
||||
float dtb = 0.0f;
|
||||
switch (buf[1] & 0x3)
|
||||
{
|
||||
case 0: dtb = 2.0f; break;
|
||||
case 1: dtb = 8.0f; break;
|
||||
case 2: dtb = 32.0f; break;
|
||||
case 3: dtb = 64.0f; break;
|
||||
default: dtb = 2.0f; break;
|
||||
}
|
||||
float df = (buf[1] >> 2) & 0x3;
|
||||
ed->average_duration_us = df * dtb;
|
||||
ed->energy_detection_value = (float)(*(int8_t*)(&buf[2]));
|
||||
}
|
||||
|
||||
//==================================================================================
|
||||
uint8_t at86rf215_radio_get_random_value(at86rf215_st* dev, at86rf215_rf_channel_en ch)
|
||||
{
|
||||
// "RG_RNDV" RFn_RNDV – Random Value - contains 8bit random value
|
||||
uint16_t reg_address = AT86RF215_REG_ADDR(ch, RNDV);
|
||||
return at86rf215_read_byte(dev, reg_address);
|
||||
}
|
||||
|
||||
//==================================================================================
|
||||
void at86rf215_radio_setup_tx_ctrl(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
at86rf215_radio_tx_ctrl_st* cfg)
|
||||
{
|
||||
/*
|
||||
RFn_TXCUTC – Transmitter Filter Cutoff Control and PA Ramp Time
|
||||
Bit 7:6 – TXCUTC.PARAMP: Power Amplifier Ramp Time
|
||||
RF_PARAMP4U 0x0 tpa_ramp = 4μs
|
||||
RF_PARAMP8U 0x1 tpa_ramp = 8μs
|
||||
RF_PARAMP16U 0x2 tpa_ramp = 16μs
|
||||
RF_PARAMP32U 0x3 tpa_ramp = 32μs
|
||||
Bit 3:0 – TXCUTC.LPFCUT: Transmitter cut-off frequency
|
||||
RF_FLC80KHZ 0x0 fLPFCUT = 80kHz
|
||||
RF_FLC100KHZ 0x1 fLPFCUT = 100kHz
|
||||
RF_FLC125KHZ 0x2 fLPFCUT = 125kHz
|
||||
RF_FLC160KHZ 0x3 fLPFCUT = 160kHz
|
||||
RF_FLC200KHZ 0x4 fLPFCUT = 200kHz
|
||||
RF_FLC250KHZ 0x5 fLPFCUT = 250kHz
|
||||
RF_FLC315KHZ 0x6 fLPFCUT = 315kHz
|
||||
RF_FLC400KHZ 0x7 fLPFCUT = 400kHz
|
||||
RF_FLC500KHZ 0x8 fLPFCUT = 500kHz
|
||||
RF_FLC625KHZ 0x9 fLPFCUT = 625kHz
|
||||
RF_FLC800KHZ 0xA fLPFCUT = 800kHz
|
||||
RF_FLC1000KHZ 0xB fLPFCUT = 1000kHz
|
||||
*/
|
||||
|
||||
/*
|
||||
RFn_TXDFE – Transmitter TX Digital Frontend
|
||||
Bit 7:5 – TXDFE.RCUT: TX filter relative to the cut-off frequency
|
||||
0x0 fCUT=0.25 *fS/2
|
||||
0x1 fCUT=0.375*fS/2
|
||||
0x2 fCUT=0.5 *fS/2
|
||||
0x3 fCUT=0.75 *fS/2
|
||||
0x4 fCUT=1.00 *fS/2
|
||||
Bit 4 – TXDFE.DM: Direct Modulation
|
||||
If enabled (1) the transmitter direct modulation is enabled - available for FSK and
|
||||
OQPSK (OQPSKC0.FCHIP=0;1). Direct modulation must also be enabled at the BBCx registers (FSKDM.EN and OQPSKC0.DM).
|
||||
Bit 3:0 – TXDFE.SR: TX Sample Rate
|
||||
0x1 fS=4000kHz
|
||||
0x2 fS=2000kHz
|
||||
0x3 fS=(4000/3)kHz
|
||||
0x4 fS=1000kHz
|
||||
0x5 fS=800kHz
|
||||
0x6 fS=(2000/3)kHz
|
||||
0x8 fS=500kHz
|
||||
0xA fS=400kHz
|
||||
*/
|
||||
|
||||
/*
|
||||
RFn_PAC – Transmitter Power Amplifier Control
|
||||
Bit 6:5 – PAC.PACUR: Power Amplifier Current Control - PACUR configures the power amplifier DC current (useful for low power settings).
|
||||
0x0 Power amplifier current reduction by about 22mA (3dB reduction of max. small signal gain)
|
||||
0x1 Power amplifier current reduction by about 18mA (2dB reduction of max. small signal gain)
|
||||
0x2 Power amplifier current reduction by about 11mA (1dB reduction of max. small signal gain)
|
||||
0x3 No power amplifier current reduction (max. transmit small signal gain)
|
||||
Bit 4:0 – PAC.TXPWR: Transmitter Output Power
|
||||
Maximum output power is TXPWR=31, minimum output power is TXPWR=0. The output power can be set by about 1dB step resolution.
|
||||
*/
|
||||
|
||||
uint16_t reg_address_txcut = AT86RF215_REG_ADDR(ch, TXCUTC);
|
||||
uint8_t buf[3] = {0};
|
||||
|
||||
buf[0] |= (cfg->pa_ramping_time & 0x3) << 6;
|
||||
buf[0] |= (cfg->analog_bw & 0xF);
|
||||
buf[1] |= (cfg->digital_bw & 0x7) << 5;
|
||||
buf[1] |= (cfg->direct_modulation & 0x1) << 4;
|
||||
buf[1] |= (cfg->fs & 0xF);
|
||||
buf[2] |= (cfg->current_reduction & 0x3) << 5;
|
||||
buf[2] |= (cfg->tx_power & 0x1F);
|
||||
|
||||
at86rf215_write_buffer(dev, reg_address_txcut, buf, 3);
|
||||
}
|
||||
|
||||
//==================================================================================
|
||||
void at86rf215_radio_get_tx_ctrl(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
at86rf215_radio_tx_ctrl_st* cfg)
|
||||
{
|
||||
uint16_t reg_address_txcut = AT86RF215_REG_ADDR(ch, TXCUTC);
|
||||
uint8_t buf[3] = {0};
|
||||
at86rf215_read_buffer(dev, reg_address_txcut, buf, 3);
|
||||
|
||||
cfg->pa_ramping_time = (buf[0] >> 6) & 0x3;
|
||||
cfg->analog_bw = (buf[0] >> 0) & 0xF;
|
||||
cfg->digital_bw = (buf[1] >> 5) & 0x7;
|
||||
cfg->direct_modulation = (buf[1] >> 4) & 0x1;
|
||||
cfg->fs = (buf[1] >> 0) & 0xF;
|
||||
cfg->current_reduction = (buf[2] >> 5) & 0x3;
|
||||
cfg->tx_power = (buf[2] >> 0) & 0x1F;
|
||||
}
|
||||
|
||||
//==================================================================================
|
||||
void at86rf215_radio_setup_pll_ctrl(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
at86rf215_radio_pll_ctrl_st* cfg)
|
||||
{
|
||||
uint16_t reg_address_pll = AT86RF215_REG_ADDR(ch, PLL);
|
||||
uint8_t buf[2] = {0};
|
||||
|
||||
buf[0] |= (cfg->loop_bw & 0x3) << 4;
|
||||
//buf[0] |= (cfg->pll_locked & 0x1) << 1; <- status
|
||||
buf[1] |= (cfg->pll_center_freq & 0x3F) << 0;
|
||||
|
||||
at86rf215_write_buffer(dev, reg_address_pll, buf, 2);
|
||||
}
|
||||
|
||||
//==================================================================================
|
||||
void at86rf215_radio_get_pll_ctrl(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
at86rf215_radio_pll_ctrl_st* cfg)
|
||||
{
|
||||
uint16_t reg_address_pll = AT86RF215_REG_ADDR(ch, PLL);
|
||||
uint8_t buf[2] = {0};
|
||||
|
||||
at86rf215_read_buffer(dev, reg_address_pll, buf, 2);
|
||||
|
||||
cfg->loop_bw = (buf[0] >> 4) & 0x3;
|
||||
cfg->pll_locked = (buf[0] >> 1) & 0x1;
|
||||
cfg->pll_center_freq = (buf[1] >> 0) & 0x3F;
|
||||
}
|
||||
|
||||
//==================================================================================
|
||||
void at86rf215_radio_set_tx_iq_calibration(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
int cal_i, int cal_q)
|
||||
{
|
||||
uint16_t reg_address_cal_i = AT86RF215_REG_ADDR(ch, TXCI);
|
||||
|
||||
uint8_t buf[2] = {0};
|
||||
buf[0] = cal_i & 0x3F;
|
||||
buf[1] = cal_q & 0x3F;
|
||||
at86rf215_write_buffer(dev, reg_address_cal_i, buf, 2);
|
||||
|
||||
// RFn_TXCI – Transmit calibration I path
|
||||
// The register contains information about the TX LO leakage calibration value of the transmit I path. At the transition
|
||||
// process from state TRXOFF to TX the calibration is started automatically
|
||||
|
||||
// RFn_TXCQ – Transmit calibration Q path
|
||||
// The register contains information about the TX LO leakage calibration value of the transmit Q path. At the transition
|
||||
// process from state TRXOFF to TX the calibration is started automatically.
|
||||
}
|
||||
|
||||
//==================================================================================
|
||||
void at86rf215_radio_get_tx_iq_calibration(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
int *cal_i, int *cal_q)
|
||||
{
|
||||
uint16_t reg_address_cal_i = AT86RF215_REG_ADDR(ch, TXCI);
|
||||
uint16_t reg_address_cal_q = AT86RF215_REG_ADDR(ch, TXCQ);
|
||||
uint8_t buf[2] = {0};
|
||||
|
||||
at86rf215_read_buffer(dev, reg_address_cal_i, buf, 2);
|
||||
|
||||
*cal_i = buf[0] & 0x3F;
|
||||
*cal_q = buf[1] & 0x3F;
|
||||
}
|
||||
|
||||
//==================================================================================
|
||||
void at86rf215_radio_set_tx_dac_input_iq(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
int enable_dac_i_dc, int dac_i_val,
|
||||
int enable_dac_q_dc, int dac_q_val)
|
||||
{
|
||||
uint16_t reg_address_dac_i = AT86RF215_REG_ADDR(ch, TXDACI);
|
||||
|
||||
uint8_t buf[2] = {0};
|
||||
buf[0] = ((enable_dac_i_dc & 0x1)<<7) | (dac_i_val & 0x7F);
|
||||
buf[1] = ((enable_dac_q_dc & 0x1)<<7) | (dac_q_val & 0x7F);
|
||||
at86rf215_write_buffer(dev, reg_address_dac_i, buf, 2);
|
||||
|
||||
// RFn_TXDACI – In-phase input value for TXDAC - Input I value can be applied at TXDAC (DC measurement)
|
||||
// Bit 7 – TXDACI.ENTXDACID: Enable input to TXDAC
|
||||
// Bit 6:0 – TXDACI.TXDACID: Input to TXDAC data
|
||||
|
||||
// RFn_TXDACQ – Quadrature-phase input value for TXDAC - Input Q value can be applied at TXDAC (DC measurement)
|
||||
// Bit 7 – TXDACQ.ENTXDACQD: Enable input to TXDAC
|
||||
// Bit 6:0 – TXDACQ.TXDACQD: Input to TXDAC data
|
||||
|
||||
/*
|
||||
PAGE 221 in datasheet!
|
||||
The AT86RF215 comprises a DAC (digital to analog converter) value overwrite functionality. Each transceiver contains
|
||||
two transmitter DACs (for the in-phase and quadrature-phase signal) in order to transmit IQ signals. Both DAC values
|
||||
can be overwritten separately by register settings. This feature is useful to transmit an LO carrier which is necessary for
|
||||
certain certifications.
|
||||
*/
|
||||
}
|
||||
|
||||
//==================================================================================
|
||||
void at86rf215_radio_get_tx_dac_input_iq(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
int *enable_dac_i_dc, int *dac_i_val,
|
||||
int *enable_dac_q_dc, int *dac_q_val)
|
||||
{
|
||||
uint16_t reg_address_dac_i = AT86RF215_REG_ADDR(ch, TXDACI);
|
||||
|
||||
uint8_t buf[2] = {0};
|
||||
at86rf215_read_buffer(dev, reg_address_dac_i, buf, 2);
|
||||
|
||||
*enable_dac_i_dc = (buf[0] >> 7) & 0x1;
|
||||
*enable_dac_q_dc = (buf[1] >> 7) & 0x1;
|
||||
|
||||
*dac_i_val = (buf[0]) & 0x3F;
|
||||
*dac_q_val = (buf[1]) & 0x3F;
|
||||
}
|
|
@ -1,374 +0,0 @@
|
|||
#ifndef __AT86RF215_RADIO_H__
|
||||
#define __AT86RF215_RADIO_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include "at86rf215_common.h"
|
||||
|
||||
struct at86rf215_radio_regs
|
||||
{
|
||||
uint16_t RG_IRQS;
|
||||
uint16_t RG_IRQM;
|
||||
uint16_t RG_AUXS;
|
||||
uint16_t RG_STATE;
|
||||
uint16_t RG_CMD;
|
||||
uint16_t RG_CS;
|
||||
uint16_t RG_CCF0L;
|
||||
uint16_t RG_CCF0H;
|
||||
uint16_t RG_CNL;
|
||||
uint16_t RG_CNM;
|
||||
uint16_t RG_RXBWC;
|
||||
uint16_t RG_RXDFE;
|
||||
uint16_t RG_AGCC;
|
||||
uint16_t RG_AGCS;
|
||||
uint16_t RG_RSSI;
|
||||
uint16_t RG_EDC;
|
||||
uint16_t RG_EDD;
|
||||
uint16_t RG_EDV;
|
||||
uint16_t RG_RNDV;
|
||||
uint16_t RG_TXCUTC;
|
||||
uint16_t RG_TXDFE;
|
||||
uint16_t RG_PAC;
|
||||
uint16_t RG_PADFE;
|
||||
uint16_t RG_PLL;
|
||||
uint16_t RG_PLLCF;
|
||||
uint16_t RG_TXCI;
|
||||
uint16_t RG_TXCQ;
|
||||
uint16_t RG_TXDACI;
|
||||
uint16_t RG_TXDACQ;
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
at86rf215_radio_cmd_nop = 0x0, // No operation (dummy)
|
||||
at86rf215_radio_cmd_sleep = 0x1, // Go to sleep
|
||||
at86rf215_radio_state_cmd_trx_off = 0x2, // Transceiver off, SPI active
|
||||
at86rf215_radio_state_cmd_tx_prep = 0x3, // Transmit preparation
|
||||
at86rf215_radio_state_cmd_tx = 0x4, // Transmit
|
||||
at86rf215_radio_state_cmd_rx = 0x5, // Receive
|
||||
at86rf215_radio_state_transition = 0x6, // State transition in progress
|
||||
at86rf215_radio_state_cmd_reset = 0x7, // Transceiver is in state RESET or SLEEP
|
||||
// if commanded reset, the transceiver state will
|
||||
// automatically end up in state TRXOFF
|
||||
} at86rf215_radio_state_cmd_en;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
at86rf215_radio_channel_mode_ieee = 0x00,
|
||||
// IEEE compliant channel scheme; f=(CCF0+CN*CS)*25kHz+f offset ;
|
||||
// (f offset = 0Hz for sub-1GHz transceiver; f offset = 1.5GHz for 2.4GHz transceiver)
|
||||
|
||||
at86rf215_radio_channel_mode_fine_low = 0x01,
|
||||
// Fine resolution (389.5-510.0)MHz with 99.182Hz channel stepping
|
||||
|
||||
at86rf215_radio_channel_mode_fine_mid = 0x02,
|
||||
// Fine resolution (779-1020)MHz with 198.364Hz channel stepping
|
||||
|
||||
at86rf215_radio_channel_mode_fine_high = 0x03,
|
||||
// Fine resolution (2400-2483.5)MHz with 396.728Hz channel stepping
|
||||
|
||||
} at86rf215_radio_channel_mode_en;
|
||||
|
||||
/** offset (in Hz) for CCF0 in 2.4 GHz mode */
|
||||
#define CCF0_24G_OFFSET 1500000U
|
||||
|
||||
typedef enum
|
||||
{
|
||||
at86rf215_radio_rx_bw_BW160KHZ_IF250KHZ = 0x0, // at86rf215_radio_rx_f_cut_0_25_half_fs
|
||||
at86rf215_radio_rx_bw_BW200KHZ_IF250KHZ = 0x1, // at86rf215_radio_rx_f_cut_0_25_half_fs
|
||||
at86rf215_radio_rx_bw_BW250KHZ_IF250KHZ = 0x2, // at86rf215_radio_rx_f_cut_0_25_half_fs
|
||||
at86rf215_radio_rx_bw_BW320KHZ_IF500KHZ = 0x3, // at86rf215_radio_rx_f_cut_0_25_half_fs
|
||||
at86rf215_radio_rx_bw_BW400KHZ_IF500KHZ = 0x4, // at86rf215_radio_rx_f_cut_0_25_half_fs
|
||||
at86rf215_radio_rx_bw_BW500KHZ_IF500KHZ = 0x5, // at86rf215_radio_rx_f_cut_0_25_half_fs
|
||||
at86rf215_radio_rx_bw_BW630KHZ_IF1000KHZ = 0x6, // at86rf215_radio_rx_f_cut_0_375_half_fs
|
||||
at86rf215_radio_rx_bw_BW800KHZ_IF1000KHZ = 0x7, // at86rf215_radio_rx_f_cut_0_5_half_fs
|
||||
at86rf215_radio_rx_bw_BW1000KHZ_IF1000KHZ = 0x8, // at86rf215_radio_rx_f_cut_0_5_half_fs
|
||||
at86rf215_radio_rx_bw_BW1250KHZ_IF2000KHZ = 0x9, // at86rf215_radio_rx_f_cut_0_75_half_fs
|
||||
at86rf215_radio_rx_bw_BW1600KHZ_IF2000KHZ = 0xA, // at86rf215_radio_rx_f_cut_half_fs
|
||||
at86rf215_radio_rx_bw_BW2000KHZ_IF2000KHZ = 0xB, // at86rf215_radio_rx_f_cut_half_fs
|
||||
at86rf215_radio_rx_bw_BW2000KHZ_IFCCKHZ = 0xC, // **
|
||||
at86rf215_radio_rx_bw_BW2000KHZ_IFDDHZ = 0xD, // **
|
||||
at86rf215_radio_rx_bw_BW2000KHZ_IFEEKHZ = 0xE, // **
|
||||
at86rf215_radio_rx_bw_BW2000KHZ_IFFFKHZ = 0xF, // **
|
||||
} at86rf215_radio_rx_bw_en;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
at86rf215_radio_rx_f_cut_0_25_half_fs = 0, // whan 4MSPS => 500 KHz
|
||||
at86rf215_radio_rx_f_cut_0_375_half_fs = 1, // whan 4MSPS => 750 KHz
|
||||
at86rf215_radio_rx_f_cut_0_5_half_fs = 2, // whan 4MSPS => 1000 KHz
|
||||
at86rf215_radio_rx_f_cut_0_75_half_fs = 3, // whan 4MSPS => 1500 KHz
|
||||
at86rf215_radio_rx_f_cut_half_fs = 4, // whan 4MSPS => 2000 KHz
|
||||
} at86rf215_radio_f_cut_en;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
at86rf215_radio_rx_sample_rate_4000khz = 0x1,
|
||||
at86rf215_radio_rx_sample_rate_2000khz = 0x2,
|
||||
at86rf215_radio_rx_sample_rate_1333khz = 0x3,
|
||||
at86rf215_radio_rx_sample_rate_1000khz = 0x4,
|
||||
at86rf215_radio_rx_sample_rate_800khz = 0x5,
|
||||
at86rf215_radio_rx_sample_rate_666khz = 0x6,
|
||||
at86rf215_radio_rx_sample_rate_500khz = 0x8,
|
||||
at86rf215_radio_rx_sample_rate_400khz = 0xA,
|
||||
} at86rf215_radio_sample_rate_en;
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
at86rf215_radio_agc_averaging_8 = 0,
|
||||
at86rf215_radio_agc_averaging_16 = 1,
|
||||
at86rf215_radio_agc_averaging_32 = 2,
|
||||
at86rf215_radio_agc_averaging_64 = 3,
|
||||
} at86rf215_radio_agc_averaging_en;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
at86rf215_radio_agc_relative_atten_21_db = 0,
|
||||
at86rf215_radio_agc_relative_atten_24_db = 1,
|
||||
at86rf215_radio_agc_relative_atten_27_db = 2,
|
||||
at86rf215_radio_agc_relative_atten_30_db = 3,
|
||||
at86rf215_radio_agc_relative_atten_33_db = 4,
|
||||
at86rf215_radio_agc_relative_atten_36_db = 5,
|
||||
at86rf215_radio_agc_relative_atten_39_db = 6,
|
||||
at86rf215_radio_agc_relative_atten_41_db = 7,
|
||||
} at86rf215_radio_agc_relative_atten_en;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// commands
|
||||
int agc_measure_source_not_filtered; // AGC Input (0 - filterred, 1 - unfiltered, faster operation)
|
||||
at86rf215_radio_agc_averaging_en avg; // AGC Average Time in Number of Samples
|
||||
int reset_cmd; // AGC Reset - resets the AGC and sets the maximum receiver gain.
|
||||
int freeze_cmd; // AGC Freeze Control - A value of one forces the AGC to
|
||||
// freeze to its current value.
|
||||
int enable_cmd; // AGC Enable - a value of zero allows a manual setting of
|
||||
// the RX gain control by sub-register AGCS.GCW
|
||||
|
||||
at86rf215_radio_agc_relative_atten_en att; // AGC Target Level - sets the AGC target level relative to ADC full scale.
|
||||
int gain_control_word; // If AGCC_EN is set to 1, a read of bit AGCS.GCW indicates the current
|
||||
// receiver gain setting. If AGCC_EN is set to 0, a write access to GCW
|
||||
// manually sets the receiver gain. An integer value of 23 indicates
|
||||
// the maximum receiver gain; each integer step changes the gain by 3dB.
|
||||
|
||||
// status
|
||||
int freeze_status; // AGC Freeze Status - A value of one indicates that the AGC is on hold.
|
||||
} at86rf215_radio_agc_ctrl_st;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int ext_lna_bypass_available; // The bit activates an AGC scheme where the external LNA
|
||||
// can be bypassed using pins FEAnn/FEBnn.
|
||||
|
||||
int agc_backoff; // 0x00 - no backoff - anyway we do not use this option
|
||||
int analog_voltage_external; // The bit disables the internal analog supply voltage - requires external LDO
|
||||
int analog_voltage_enable_in_off; // for fast turn-on from TRXOFF state - the voltage is kept going even when off
|
||||
int int_power_amplifier_voltage; // 0: 2V, 1: 2.2V, 2: 2.4V;
|
||||
int fe_pad_configuration; // 2 bits, 4 options pg.73 in datasheet. for Caribou this should be "1"
|
||||
// 0: no control - pads disabled (always '0')
|
||||
// 1: FEA|FEB: L|L: TXPREP (Frontend off); H|L: TX (Transmit); L|H: RX (Receive); H|H: RX with LNA Bypass
|
||||
// 2: FEA|FEB: L|L: TXPREP (Frontend off); H|H: TX (Transmit); L|H: RX (Receive); H|L: RX with LNA Bypass
|
||||
// 3: FEA|FEB|MCUPin: L|L|L: TXPREP (Frontend off); H|H|H: TX (Transmit); L|H|H: RX (Receive); L|L|H: RX with LNA Bypass
|
||||
|
||||
// statuses
|
||||
int analog_voltage_status;
|
||||
} at86rf215_radio_external_ctrl_st;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int inverter_sign_if; // A value of one configures the receiver to implement the
|
||||
// inverted-sign IF frequency. Use default setting for normal operation.
|
||||
int shift_if_freq; // A value of one configures the receiver to shift the IF frequency
|
||||
// by factor of 1.25. This is useful to place the image frequency according
|
||||
// to channel scheme.
|
||||
at86rf215_radio_rx_bw_en bw; // The sub-register controls the receiver filter bandwidth settings.
|
||||
at86rf215_radio_f_cut_en fcut; // RX filter relative cut-off frequency
|
||||
at86rf215_radio_sample_rate_en fs; // RX Sample Rate
|
||||
} at86rf215_radio_set_rx_bw_samp_st;
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
at86rf215_radio_energy_detection_mode_auto = 0,
|
||||
// Energy detection measurement is automatically triggered if the AGC is held by the internal
|
||||
// baseband or by setting bit FRZC.
|
||||
|
||||
at86rf215_radio_energy_detection_mode_single = 1,
|
||||
// A single energy detection measurement is started.
|
||||
|
||||
at86rf215_radio_energy_detection_mode_continous = 2,
|
||||
// A continuous energy detection measurements of configured interval defined in
|
||||
// register EDD is started.
|
||||
|
||||
at86rf215_radio_energy_detection_mode_off = 3,
|
||||
// Energy detection measurement is disabled
|
||||
|
||||
} at86rf215_radio_energy_detection_mode_en;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// cmd
|
||||
at86rf215_radio_energy_detection_mode_en mode; // Energy Detection Mode
|
||||
float average_duration_us; // T[μs]=DF*DTB - the DTB will be calculated accordingly
|
||||
|
||||
// status
|
||||
float energy_detection_value; // Receiver Energy Detection Value
|
||||
} at86rf215_radio_energy_detection_st;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
at86rf215_radio_tx_pa_ramp_4usec = 0,
|
||||
at86rf215_radio_tx_pa_ramp_8usec = 1,
|
||||
at86rf215_radio_tx_pa_ramp_16usec = 2,
|
||||
at86rf215_radio_tx_pa_ramp_32usec = 3,
|
||||
} at86rf215_radio_tx_pa_ramp_en;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
at86rf215_radio_tx_cut_off_80khz = 0x0,
|
||||
at86rf215_radio_tx_cut_off_100khz = 0x1,
|
||||
at86rf215_radio_tx_cut_off_125khz = 0x2,
|
||||
at86rf215_radio_tx_cut_off_160khz = 0x3,
|
||||
at86rf215_radio_tx_cut_off_200khz = 0x4,
|
||||
at86rf215_radio_tx_cut_off_250khz = 0x5,
|
||||
at86rf215_radio_tx_cut_off_315khz = 0x6,
|
||||
at86rf215_radio_tx_cut_off_400khz = 0x7,
|
||||
at86rf215_radio_tx_cut_off_500khz = 0x8,
|
||||
at86rf215_radio_tx_cut_off_625khz = 0x9,
|
||||
at86rf215_radio_tx_cut_off_800khz = 0xA,
|
||||
at86rf215_radio_tx_cut_off_1000khz = 0xB,
|
||||
} at86rf215_radio_tx_cut_off_en;
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
at86rf215_radio_pa_current_reduction_22ma = 0, // 3dB reduction of gain
|
||||
at86rf215_radio_pa_current_reduction_18ma = 1, // 2dB reduction of gain
|
||||
at86rf215_radio_pa_current_reduction_11ma = 2, // 1dB reduction of gain
|
||||
at86rf215_radio_pa_current_reduction_0ma = 3, // no reduction
|
||||
} at86rf215_radio_pa_current_reduction_en;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
at86rf215_radio_tx_pa_ramp_en pa_ramping_time;
|
||||
at86rf215_radio_pa_current_reduction_en current_reduction;
|
||||
int tx_power;
|
||||
// Maximum output power is TXPWR=31, minimum output power is TXPWR=0.
|
||||
// The output power can be set by about 1dB step resolution.
|
||||
|
||||
at86rf215_radio_tx_cut_off_en analog_bw;
|
||||
at86rf215_radio_f_cut_en digital_bw;
|
||||
at86rf215_radio_sample_rate_en fs;
|
||||
int direct_modulation;
|
||||
|
||||
} at86rf215_radio_tx_ctrl_st;
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
at86rf215_radio_pll_loop_bw_default = 0,
|
||||
at86rf215_radio_pll_loop_bw_dec_15perc = 1,
|
||||
at86rf215_radio_pll_loop_bw_inc_15perc = 2,
|
||||
} at86rf215_radio_pll_loop_bw_en;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
at86rf215_radio_pll_loop_bw_en loop_bw;
|
||||
// This sub-register controls the PLL loop bandwidth.The sub-register is applicable for the sub-1GHz transceiver only
|
||||
// (RF09). The TX modulation quality (i.e. FSK eye diagram) can be adjusted when direct modulation is used.
|
||||
|
||||
int pll_center_freq;
|
||||
// Center frequency calibration is performed from state TRXOFF to state TXPREP and at channel change. The register
|
||||
// displays the center frequency value of the current channel.
|
||||
|
||||
// statuses
|
||||
int pll_locked;
|
||||
} at86rf215_radio_pll_ctrl_st;
|
||||
|
||||
|
||||
void at86rf215_radio_setup_interrupt_mask(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
at86rf215_radio_irq_st* mask);
|
||||
|
||||
void at86rf215_radio_setup_external_settings(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
at86rf215_radio_external_ctrl_st* cfg);
|
||||
|
||||
void at86rf215_radio_get_external_settings(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
at86rf215_radio_external_ctrl_st* cfg);
|
||||
|
||||
at86rf215_radio_state_cmd_en at86rf215_radio_get_state(at86rf215_st* dev, at86rf215_rf_channel_en ch);
|
||||
|
||||
void at86rf215_radio_set_state(at86rf215_st* dev, at86rf215_rf_channel_en ch, at86rf215_radio_state_cmd_en cmd);
|
||||
|
||||
double at86rf215_radio_get_frequency( /*IN*/ at86rf215_radio_channel_mode_en mode,
|
||||
/*IN*/ int channel_spacing_25khz_res,
|
||||
/*IN*/ double wanted_frequency_hz,
|
||||
/*OUT*/ int *center_freq_25khz_res,
|
||||
/*OUT*/ int *channel_number);
|
||||
|
||||
void at86rf215_radio_setup_channel(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
int channel_spacing_25khz_res,
|
||||
int center_freq_25khz_res,
|
||||
int channel_number,
|
||||
at86rf215_radio_channel_mode_en mode);
|
||||
|
||||
void at86rf215_radio_set_rx_bandwidth_sampling(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
at86rf215_radio_set_rx_bw_samp_st* cfg);
|
||||
|
||||
void at86rf215_radio_get_rx_bandwidth_sampling(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
at86rf215_radio_set_rx_bw_samp_st* cfg);
|
||||
|
||||
void at86rf215_radio_setup_agc(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
at86rf215_radio_agc_ctrl_st *agc_ctrl);
|
||||
|
||||
void at86rf215_radio_get_agc(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
at86rf215_radio_agc_ctrl_st *agc_ctrl);
|
||||
|
||||
float at86rf215_radio_get_rssi_dbm(at86rf215_st* dev, at86rf215_rf_channel_en ch);
|
||||
|
||||
void at86rf215_radio_setup_energy_detection(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
at86rf215_radio_energy_detection_st* ed);
|
||||
|
||||
void at86rf215_radio_get_energy_detection(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
at86rf215_radio_energy_detection_st* ed);
|
||||
|
||||
uint8_t at86rf215_radio_get_random_value(at86rf215_st* dev, at86rf215_rf_channel_en ch);
|
||||
|
||||
void at86rf215_radio_setup_tx_ctrl(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
at86rf215_radio_tx_ctrl_st* cfg);
|
||||
|
||||
void at86rf215_radio_get_tx_ctrl(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
at86rf215_radio_tx_ctrl_st* cfg);
|
||||
|
||||
void at86rf215_radio_setup_pll_ctrl(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
at86rf215_radio_pll_ctrl_st* cfg);
|
||||
|
||||
void at86rf215_radio_get_pll_ctrl(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
at86rf215_radio_pll_ctrl_st* cfg);
|
||||
|
||||
void at86rf215_radio_set_tx_iq_calibration(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
int cal_i, int cal_q);
|
||||
|
||||
void at86rf215_radio_get_tx_iq_calibration(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
int *cal_i, int *cal_q);
|
||||
|
||||
void at86rf215_radio_set_tx_dac_input_iq(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
int enable_dac_i_dc, int dac_i_val,
|
||||
int enable_dac_q_dc, int dac_q_val);
|
||||
|
||||
void at86rf215_radio_get_tx_dac_input_iq(at86rf215_st* dev, at86rf215_rf_channel_en ch,
|
||||
int *enable_dac_i_dc, int *dac_i_val,
|
||||
int *enable_dac_q_dc, int *dac_q_val);
|
||||
int at86rf215_radio_get_good_channel(double wanted_frequency_hz, at86rf215_radio_channel_mode_en *mode,
|
||||
at86rf215_rf_channel_en *ch);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __AT86RF215_RADIO_H__
|
|
@ -1,300 +0,0 @@
|
|||
#ifndef _AT86RF215_REGS_H_
|
||||
#define _AT86RF215_REGS_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* ============================================================================
|
||||
* AT86RF215 Internal registers Address
|
||||
* ============================================================================
|
||||
*/
|
||||
|
||||
/* Common settings */
|
||||
#define REG_RF_RST 0x0005
|
||||
|
||||
/* Interrupt registers */
|
||||
#define REG_RF_CFG 0x0006
|
||||
#define REG_RF09_IRQS 0x0000
|
||||
#define REG_RF24_IRQS 0x0001
|
||||
#define REG_BBC0_IRQS 0x0002
|
||||
#define REG_BBC1_IRQS 0x0003
|
||||
|
||||
#define REG_RF09_IRQM 0x0100
|
||||
#define REG_RF24_IRQM 0x0200
|
||||
#define REG_BBC0_IRQM 0x0300
|
||||
#define REG_BBC1_IRQM 0x0400
|
||||
|
||||
/* RF interrupt register values */
|
||||
#define RF_IRQM_WAKEUP 0x00
|
||||
#define RF_IRQM_TRXRDY 0x01
|
||||
#define RF_IRQM_EDC 0x02
|
||||
#define RF_IRQM_BATLOW 0x03
|
||||
#define RF_IRQM_TRXERR 0x04
|
||||
#define RF_IRQM_IQIFSF 0x05
|
||||
|
||||
/* BB interrupt register values */
|
||||
#define BB_INTR_RXFS 0x01
|
||||
#define BB_INTR_RXFE 0x02
|
||||
#define BB_INTR_RXAM 0x04
|
||||
#define BB_INTR_RXEM 0x08
|
||||
#define BB_INTR_TXFE 0x10
|
||||
#define BB_INTR_AGCH 0x20
|
||||
#define BB_INTR_AGCR 0x40
|
||||
#define BB_INTR_FBLI 0x80
|
||||
|
||||
#define REG_RF_IQIFC0 0x000A
|
||||
#define REG_RF_IQIFC1 0x000B
|
||||
#define REG_RF_IQIFC2 0x000C
|
||||
|
||||
/* Baseband registers */
|
||||
#define REG_BBC0_TXFLL 0x0306
|
||||
#define REG_BBC0_TXFLH 0x0307
|
||||
#define REG_BBC0_FBTXS 0x2800
|
||||
#define REG_BBC0_FBTXE 0x2FFE
|
||||
#define REG_BBC0_FSKPHRTX 0x036A
|
||||
#define REG_BBC0_PC 0x0301
|
||||
#define REG_BBC0_FSKDM 0x0372
|
||||
|
||||
#define REG_BBC1_TXFLL 0x0406
|
||||
#define REG_BBC1_TXFLH 0x0407
|
||||
#define REG_BBC1_FBTXS 0x3800
|
||||
#define REG_BBC1_FBTXE 0x3FFE
|
||||
#define REG_BBC1_FSKPHRTX 0x046A
|
||||
#define REG_BBC1_TXDFE 0x0113
|
||||
#define REG_BBC1_PC 0x0401
|
||||
#define REG_BBC1_FSKDM 0x0372
|
||||
|
||||
/* Common RF */
|
||||
#define REG_RF09_PLLCF 0x0122
|
||||
#define REG_RF_CLKO 0x0007
|
||||
#define RF_CLKO_OFF 0x00
|
||||
typedef enum
|
||||
{
|
||||
at86rf215_drive_current_2ma = 0,
|
||||
at86rf215_drive_current_4ma = 1,
|
||||
at86rf215_drive_current_6ma = 2,
|
||||
at86rf215_drive_current_8ma = 3,
|
||||
} at86rf215_drive_current_en;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
at86rf215_clock_out_freq_off = 0,
|
||||
at86rf215_clock_out_freq_26mhz = 1,
|
||||
at86rf215_clock_out_freq_32mhz = 2,
|
||||
at86rf215_clock_out_freq_16mhz = 3,
|
||||
at86rf215_clock_out_freq_8mhz = 4,
|
||||
at86rf215_clock_out_freq_4mhz = 5,
|
||||
at86rf215_clock_out_freq_2mhz = 6,
|
||||
at86rf215_clock_out_freq_1mhz = 7,
|
||||
} at86rf215_clock_out_freq_en;
|
||||
|
||||
/* bandwidth */
|
||||
#define REG_RF09_RXBWC 0x0109
|
||||
#define REG_RF24_RXBWC 0x0209
|
||||
|
||||
/* Front end FE Pads */
|
||||
#define REG_RF09_PADFE 0x0116
|
||||
#define REG_RF24_PADFE 0x0216
|
||||
|
||||
/* different bandwidth values */
|
||||
#define RF_BW160KHZ_IF250KHZ 0x00
|
||||
#define RF_BW200KHZ_IF250KHZ 0x01
|
||||
#define RF_BW250KHZ_IF250KHZ 0x02
|
||||
#define RF_BW320KHZ_IF500KHZ 0x03
|
||||
#define RF_BW400KHZ_IF500KHZ 0x04
|
||||
#define RF_BW500KHZ_IF500KHZ 0x05
|
||||
#define RF_BW630KHZ_IF1000KHZ 0x06
|
||||
#define RF_BW800KHZ_IF1000KHZ 0x07
|
||||
#define RF_BW1000KHZ_IF1000KHZ 0x08
|
||||
#define RF_BW1250KHZ_IF2000KHZ 0x09
|
||||
#define RF_BW1600KHZ_IF2000KHZ 0x0A
|
||||
#define RF_BW2000KHZ_IF2000KHZ 0x0B
|
||||
#define RF_BW2000KHZ_IFCCKHZ 0x0C
|
||||
#define RF_BW2000KHZ_IFDDHZ 0x0D
|
||||
#define RF_BW2000KHZ_IFEEKHZ 0x0E
|
||||
#define RF_BW2000KHZ_IFFFKHZ 0x0F
|
||||
|
||||
/* different Skew values */
|
||||
#define RF_IQ_SKEW_pos_2 0x00
|
||||
#define RF_IQ_SKEW_pos_1 0x01
|
||||
#define RF_IQ_SKEW_zero 0x02
|
||||
#define RF_IQ_SKEW_neg_1 0x03
|
||||
|
||||
/* CMV values */
|
||||
#define RF_IQ_LVDS_CMV150 0x00
|
||||
#define RF_IQ_LVDS_CMV200 0x01
|
||||
#define RF_IQ_LVDS_CMV250 0x02
|
||||
#define RF_IQ_LVDS_CMV300 0x03
|
||||
|
||||
/* IQ drive current */
|
||||
#define RF_IQ_DRV_Current_1mA 0x00
|
||||
#define RF_IQ_DRV_Current_2mA 0x01
|
||||
#define RF_IQ_DRV_Current_3mA 0x02
|
||||
#define RF_IQ_DRV_Current_4mA 0x03
|
||||
|
||||
/* different IFS values */
|
||||
#define RX_IFS_Deactive 0x00
|
||||
#define RX_IFS_Active 0x01
|
||||
|
||||
/* sampling rate */
|
||||
#define REG_RF09_RXDFE 0x010A
|
||||
#define REG_RF24_RXDFE 0x020A
|
||||
|
||||
/* different sampling rate values */
|
||||
#define RF_SR4000 0x01
|
||||
#define RF_SR2000 0x02
|
||||
#define RF_SR1333 0x03
|
||||
#define RF_SR1000 0x04
|
||||
#define RF_SR800 0x05
|
||||
#define RF_SR666 0x06
|
||||
#define RF_SR500 0x08
|
||||
#define RF_SR400 0x0A
|
||||
|
||||
/* RX filter relative cut-off frequency */
|
||||
#define RF_CUT_1_4 0x00
|
||||
#define RF_CUT_3_8 0x01
|
||||
#define RF_CUT_1_2 0x02
|
||||
#define RF_CUT_3_4 0x03
|
||||
#define RF_CUT_4_4 0x04
|
||||
|
||||
/* AGC */
|
||||
#define REG_RF09_AGCC 0x010B
|
||||
#define REG_RF09_AGCS 0x010C
|
||||
#define REG_RF24_AGCC 0x020B
|
||||
#define REG_RF24_AGCS 0x020C
|
||||
|
||||
/* AGC values */
|
||||
|
||||
/* AGC average sampling */
|
||||
#define RF_AGC_AVGS_8 0x00
|
||||
#define RF_AGC_AVGS_16 0x01
|
||||
#define RF_AGC_AVGS_32 0x02
|
||||
#define RF_AGC_AVGS_64 0x03
|
||||
|
||||
/* Chip mode */
|
||||
#define RF_MODE_BBRF 0x00
|
||||
#define RF_MODE_RF 0x01
|
||||
#define RF_MODE_BBRF09 0x04
|
||||
#define RF_MODE_BBRF24 0x05
|
||||
|
||||
/* Transceiver operation commands */
|
||||
#define RF_CMD_NOP 0x00
|
||||
#define RF_CMD_SLEEP 0x01
|
||||
#define RF_CMD_TRXOFF 0x02
|
||||
#define RF_CMD_TXPREP 0x03
|
||||
#define RF_CMD_TX 0x04
|
||||
#define RF_CMD_RX 0x05
|
||||
#define RF_CMD_RESET 0x07
|
||||
|
||||
/* Transceiver Baseband PHY type, combined BBEN and PT part of PHY Control register */
|
||||
#define BB_PHY_PHYOFF 0x00
|
||||
#define BB_PHY_FSK 0x05
|
||||
#define BB_PHY_OFDM 0x06
|
||||
#define BB_PHY_OQPSK 0x07
|
||||
|
||||
/* Transceiver interface mode */
|
||||
#define RF_MODE_BBRF 0x00
|
||||
#define RF_MODE_RF 0x01
|
||||
#define RF_MODE_BBRF09 0x04
|
||||
#define RF_MODE_BBRF24 0x05
|
||||
|
||||
/* Oscillator settings */
|
||||
#define REG_RF_XOC 0x0009
|
||||
|
||||
/* Command */
|
||||
|
||||
/* RF09 Radio */
|
||||
#define REG_RF09_AUXS 0x0101
|
||||
#define REG_RF09_STATE 0x0102
|
||||
#define REG_RF09_CMD 0x0103
|
||||
#define REG_RF09_PAC 0x0114
|
||||
#define REG_RF09_TXDFE 0x0113
|
||||
|
||||
/* RF24 Radio */
|
||||
#define REG_RF24_AUXS 0x0201
|
||||
#define REG_RF24_CMD 0x0203
|
||||
#define REG_RF24_STATE 0x0202
|
||||
#define REG_RF24_PAC 0x0214
|
||||
#define REG_RF24_TXDFE 0x0213
|
||||
|
||||
/* power levels */
|
||||
#define RF_TXPWR_00 0x00 //-21.4dBm
|
||||
#define RF_TXPWR_01 0x01 //-20.4dBm
|
||||
#define RF_TXPWR_10 0x10 //
|
||||
#define RF_TXPWR_MAX 0x1F //
|
||||
|
||||
/* power amplifier current reduction */
|
||||
#define RF_PAC_3dB_Reduction 0x00
|
||||
#define RF_PAC_2dB_Reduction 0x01
|
||||
#define RF_PAC_1dB_Reduction 0x02
|
||||
#define RF_PAC_0dB_Reduction 0x03
|
||||
|
||||
/* power amplifier DC voltage */
|
||||
#define RF_PA_VC_2_0 0x00
|
||||
#define RF_PA_VC_2_2 0x01
|
||||
#define RF_PA_VC_2_4 0x02
|
||||
|
||||
/* frequency registers */
|
||||
/* 900MHz */
|
||||
#define REG_RF09_CS 0x0104
|
||||
#define REG_RF09_CCF0L 0x0105
|
||||
#define REG_RF09_CCF0H 0x0106
|
||||
#define REG_RF09_CNL 0x0107
|
||||
#define REG_RF09_CNM 0x0108
|
||||
/* 2400MHz */
|
||||
#define REG_RF24_CS 0x0204
|
||||
#define REG_RF24_CCF0L 0x0205
|
||||
#define REG_RF24_CCF0H 0x0206
|
||||
#define REG_RF24_CNL 0x0207
|
||||
#define REG_RF24_CNM 0x0208
|
||||
|
||||
/* Calibration registers */
|
||||
/* RF09 Interface */
|
||||
#define REG_RF09_TXCI 0x0125
|
||||
#define REG_RF09_TXCQ 0x0126
|
||||
|
||||
/* RF24 Interface */
|
||||
#define REG_RF24_TXCI 0x0225
|
||||
#define REG_RF24_TXCQ 0x0226
|
||||
|
||||
/* RF24 Radio */
|
||||
#define REG_RF24_CMD 0x0203
|
||||
|
||||
/* I/O settings */
|
||||
|
||||
/* Version */
|
||||
#define REG_RF_VN 0x000E
|
||||
#define REG_RF_PN 0x000D
|
||||
|
||||
typedef enum
|
||||
{
|
||||
at86rf215_pn_at86rf215 = 0x34,
|
||||
at86rf215_pn_at86rf215iq = 0x35,
|
||||
at86rf215_pn_at86rf215m = 0x36,
|
||||
} at86rf215_pn_en;
|
||||
|
||||
/* Additional settings */
|
||||
|
||||
/*!
|
||||
* ============================================================================
|
||||
* Timing Definitions
|
||||
* Timings are define in nano seconds
|
||||
* Page 189 datasheet
|
||||
* ============================================================================
|
||||
*/
|
||||
#define T_TRXOFF_TXPREP_ns 200
|
||||
#define T_TXPREP_TRXOFF_ns 200
|
||||
#define T_TXPREP_TX_ns 200
|
||||
#define T_TXPREP_RX_ns 200
|
||||
#define T_TX_TXPREP_TXFE_ns 200
|
||||
#define T_TX_TXPREP_CMD_us 33 //assuming PA ramp 3
|
||||
#define T_RX_TXPREP_ns 200
|
||||
#define T_TX_Start_Delay_us 4
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -1,284 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include "at86rf215.h"
|
||||
#include "at86rf215_radio.h"
|
||||
#include "io_utils/io_utils.h"
|
||||
#include "io_utils/io_utils_spi.h"
|
||||
|
||||
#define CARIBOULITE_MOSI 20
|
||||
#define CARIBOULITE_SCK 21
|
||||
#define CARIBOULITE_MISO 19
|
||||
|
||||
#define FPGA_RESET 26
|
||||
#define ICE40_CS 18
|
||||
#define MIXER_RESET 5
|
||||
#define CARIBOULITE_SPI_DEV 1
|
||||
#define CARIBOULITE_MODEM_SPI_CHANNEL 1
|
||||
|
||||
#define CARIBOULITE_MODEM_SS 17
|
||||
#define CARIBOULITE_MODEM_RESET 23
|
||||
#define CARIBOULITE_MODEM_IRQ 22
|
||||
|
||||
|
||||
io_utils_spi_st io_spi_dev =
|
||||
{
|
||||
.miso = CARIBOULITE_MISO,
|
||||
.mosi = CARIBOULITE_MOSI,
|
||||
.sck = CARIBOULITE_SCK,
|
||||
};
|
||||
|
||||
at86rf215_st dev =
|
||||
{
|
||||
.reset_pin = CARIBOULITE_MODEM_RESET,
|
||||
.irq_pin = CARIBOULITE_MODEM_IRQ,
|
||||
.cs_pin = CARIBOULITE_MODEM_SS,
|
||||
.spi_dev = CARIBOULITE_SPI_DEV,
|
||||
.spi_channel = CARIBOULITE_MODEM_SPI_CHANNEL,
|
||||
};
|
||||
|
||||
|
||||
|
||||
uint16_t unknown_regs[] = { 0x0015,
|
||||
0x0115, 0x0117, 0x0118, 0x0119, 0x011A, 0x011B, 0x011C, 0x011D, 0x011E, 0x011F, 0x0120, 0x0123, 0x0124, 0x0129,
|
||||
0x0215, 0x0217, 0x0218, 0x0219, 0x021A, 0x021B, 0x021C, 0x021D, 0x021E, 0x021F, 0x0220, 0x0223, 0x0224, 0x0229};
|
||||
|
||||
uint8_t defaults[] = { 0x0E,
|
||||
0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0f, 0x0f, 0x08,
|
||||
0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0f, 0x0f, 0x08};
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// This test checks the version number and part numbers of the device
|
||||
int test_at86rf215_read_all_regs(at86rf215_st* dev)
|
||||
{
|
||||
for (uint16_t reg = 0; reg < 0x300; reg ++)
|
||||
{
|
||||
uint8_t val = at86rf215_read_byte(dev, reg);
|
||||
printf("REG #0x%04X : 0x%02X\n", reg, val);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int test_at86rf215_read_all_regs_check(at86rf215_st* dev)
|
||||
{
|
||||
int num_regs = sizeof(unknown_regs) / sizeof(uint16_t);
|
||||
|
||||
for (int i = 0; i < num_regs; i ++)
|
||||
{
|
||||
uint8_t val = at86rf215_read_byte(dev, unknown_regs[i]);
|
||||
if (val != defaults[i]) printf("REG #0x%04X : 0x%02X (instead of 0x%02X)\n", unknown_regs[i], val, defaults[i]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// This test checks the version number and part numbers of the device
|
||||
int test_at86rf215_read_chip_vn_pn(at86rf215_st* dev)
|
||||
{
|
||||
int pass = 0;
|
||||
uint8_t pn, vn;
|
||||
char pn_st[15] = {0};
|
||||
at86rf215_get_versions(dev, &pn, &vn);
|
||||
|
||||
if (pn==0x34 && vn > 0 && vn < 5) pass = 1;
|
||||
if (pn==0x34)
|
||||
sprintf(pn_st, "AT86RF215");
|
||||
else if (pn==0x35)
|
||||
sprintf(pn_st, "AT86RF215IQ");
|
||||
else if (pn==0x36)
|
||||
sprintf(pn_st, "AT86RF215M");
|
||||
else
|
||||
sprintf(pn_st, "UNKNOWN");
|
||||
|
||||
printf("TEST:AT86RF215:VERSIONS:PN=0x%02X\n", pn);
|
||||
printf("TEST:AT86RF215:VERSIONS:VN=%d\n", vn);
|
||||
printf("TEST:AT86RF215:VERSIONS:PASS=%d\n", pass);
|
||||
printf("TEST:AT86RF215:VERSIONS:INFO=The component PN is %s (0x%02X), Version %d\n", pn_st, pn, vn);
|
||||
|
||||
at86rf215_set_clock_output(dev, at86rf215_drive_current_8ma, at86rf215_clock_out_freq_32mhz);
|
||||
|
||||
return pass;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// This test performs a frequency sweep on a certain channel:
|
||||
// at86rf215_rf_channel_900mhz / at86rf215_rf_channel_2400mhz
|
||||
// and does it from "start_freq" to "start_freq + num_freq * step_freq"
|
||||
// usec_gaps - specifies the micro-second gaps between freq steps or '-1' that
|
||||
// tell the function to put "getchars" (wait for enter key)
|
||||
void test_at86rf215_sweep_frequencies(at86rf215_st* dev,
|
||||
at86rf215_rf_channel_en channel,
|
||||
int start_freq,
|
||||
int num_freq,
|
||||
int step_freq,
|
||||
int usec_gaps)
|
||||
{
|
||||
char channel_name[10];
|
||||
|
||||
if (channel == at86rf215_rf_channel_900mhz)
|
||||
sprintf(channel_name, "900MHz");
|
||||
else sprintf(channel_name, "2400MHz");
|
||||
|
||||
io_utils_usleep(usec_gaps);
|
||||
int stop_freq = start_freq + step_freq*num_freq;
|
||||
int f = start_freq;
|
||||
while (f <= stop_freq)
|
||||
{
|
||||
printf("TEST:AT86RF215:FREQ_SWEEP:FREQ=%.2fMHz, CH=%s)\n", ((float)f) / 1e6, channel_name);
|
||||
at86rf215_setup_iq_radio_dac_value_override (dev,
|
||||
channel,
|
||||
f,
|
||||
31);
|
||||
|
||||
|
||||
//printf("Press enter to switch\n");
|
||||
if (usec_gaps > 0) io_utils_usleep(usec_gaps);
|
||||
else
|
||||
{
|
||||
printf("Press enter to step...\n");
|
||||
getchar();
|
||||
}
|
||||
|
||||
//test_at86rf215_read_all_regs_check(dev);
|
||||
f += step_freq;
|
||||
}
|
||||
|
||||
// back to TX off state
|
||||
at86rf215_radio_set_state(dev, at86rf215_rf_channel_900mhz, at86rf215_radio_state_cmd_trx_off);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// Starting a reception window
|
||||
// usec_timeout - set up a timeout value in micro-seconds or -1 to wait for "enter" key
|
||||
int test_at86rf215_continues_iq_rx (at86rf215_st* dev, at86rf215_rf_channel_en radio,
|
||||
uint64_t freq_hz, int usec_timeout)
|
||||
{
|
||||
at86rf215_iq_clock_data_skew_en skew = radio == at86rf215_rf_channel_900mhz?
|
||||
at86rf215_iq_clock_data_skew_4_906ns:
|
||||
at86rf215_iq_clock_data_skew_4_906ns;
|
||||
|
||||
at86rf215_setup_iq_radio_receive (dev, radio, freq_hz, 0, skew);
|
||||
printf("Started I/Q RX session for Radio %d, Freq: %llu Hz, timeout: %d usec (0=infinity)\n",
|
||||
radio, freq_hz, usec_timeout);
|
||||
|
||||
|
||||
test_at86rf215_read_all_regs_check(dev);
|
||||
|
||||
if (usec_timeout>0)
|
||||
{
|
||||
io_utils_usleep(usec_timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Press enter to stop...\n");
|
||||
getchar();
|
||||
}
|
||||
at86rf215_stop_iq_radio_receive (dev, radio);
|
||||
|
||||
test_at86rf215_read_all_regs_check(dev);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// SMI communication tesing with loopback
|
||||
// usec_timeout - set up a timeout value in micro-seconds or -1 to wait for "enter" key
|
||||
int test_at86rf215_continues_iq_loopback (at86rf215_st* dev, at86rf215_rf_channel_en radio,
|
||||
uint32_t freq_hz, int usec_timeout)
|
||||
{
|
||||
at86rf215_setup_iq_radio_receive (dev, radio, freq_hz, 0, at86rf215_iq_clock_data_skew_4_906ns);
|
||||
//at86rf215_radio_set_tx_dac_input_iq(dev, radio, 1, 0x7E, 1, 0x3F);
|
||||
|
||||
printf("Started I/Q RX session for Radio %d, Freq: %d Hz, timeout: %d usec (0=infinity)\n",
|
||||
radio, freq_hz, usec_timeout);
|
||||
|
||||
if (usec_timeout>0)
|
||||
{
|
||||
io_utils_usleep(usec_timeout);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Press enter to stop...\n");
|
||||
getchar();
|
||||
}
|
||||
at86rf215_stop_iq_radio_receive (dev, radio);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// TEST SELECTION
|
||||
// -----------------------------------------------------------------------------------------
|
||||
#define NO_FPGA_MODE 1
|
||||
#define TEST_VERSIONS 1
|
||||
#define TEST_FREQ_SWEEP 0
|
||||
#define TEST_IQ_RX_WIND 1
|
||||
#define TEST_IQ_RX_WIND_RAD 0
|
||||
#define TEST_IQ_LB_WIND 0
|
||||
#define TEST_READ_ALL_REGS 0
|
||||
|
||||
// -----------------------------------------------------------------------------------------
|
||||
// MAIN
|
||||
// -----------------------------------------------------------------------------------------
|
||||
int main ()
|
||||
{
|
||||
at86rf215_iq_interface_config_st cfg = {0};
|
||||
at86rf215_irq_st irq = {0};
|
||||
|
||||
// Init GPIOs and set FPGA on reset
|
||||
io_utils_setup();
|
||||
|
||||
#if NO_FPGA_MODE
|
||||
// When the FPGA wakes up non-programmed, it starts interogating
|
||||
// the SPI interface for SPI-Flash hosted firmware. This process
|
||||
// interfered with other bus users. Thus in the case that the FPGA
|
||||
// is not progreammed, we need it to be in RESET state.
|
||||
// As soon as the FPGA is programmed, it behaves and doesn't interfere
|
||||
// anymore with other ICs so this code is not anymore relevant.
|
||||
io_utils_set_gpio_mode(FPGA_RESET, io_utils_alt_gpio_out);
|
||||
io_utils_set_gpio_mode(ICE40_CS, io_utils_alt_gpio_out);
|
||||
io_utils_write_gpio(FPGA_RESET, 0);
|
||||
io_utils_write_gpio(ICE40_CS, 0);
|
||||
|
||||
io_utils_set_gpio_mode(MIXER_RESET, io_utils_alt_gpio_out);
|
||||
io_utils_write_gpio(MIXER_RESET, 0);
|
||||
#endif
|
||||
|
||||
// Init spi
|
||||
io_utils_spi_init(&io_spi_dev);
|
||||
|
||||
at86rf215_reset(&dev);
|
||||
at86rf215_init(&dev, &io_spi_dev);
|
||||
|
||||
io_utils_spi_print_setup(&io_spi_dev);
|
||||
|
||||
test_at86rf215_read_all_regs_check(&dev);
|
||||
|
||||
// TEST: read the p/n and v/n from the IC
|
||||
#if TEST_VERSIONS
|
||||
test_at86rf215_read_chip_vn_pn(&dev);
|
||||
#endif // TEST_VERSIONS
|
||||
|
||||
// TEST: Frequency sweep
|
||||
#if TEST_FREQ_SWEEP
|
||||
test_at86rf215_sweep_frequencies(&dev, at86rf215_rf_channel_900mhz, 900e6, 3, 10, 10000);
|
||||
#endif // TEST_FREQ_SWEEP
|
||||
|
||||
#if TEST_IQ_RX_WIND
|
||||
#if TEST_IQ_RX_WIND_RAD==0
|
||||
test_at86rf215_continues_iq_rx (&dev, at86rf215_rf_channel_900mhz, 900e6, -1);
|
||||
#else
|
||||
test_at86rf215_continues_iq_rx (&dev, at86rf215_rf_channel_2400mhz, 2400e6, -1);
|
||||
#endif
|
||||
#endif // TEST_IQ_RX_WIND
|
||||
|
||||
#if TEST_IQ_LB_WIND
|
||||
test_at86rf215_continues_iq_loopback (&dev, at86rf215_rf_channel_900mhz, 900e6, -1);
|
||||
#endif
|
||||
|
||||
#if TEST_READ_ALL_REGS
|
||||
test_at86rf215_read_all_regs_check(&dev);
|
||||
#endif
|
||||
|
||||
at86rf215_close(&dev);
|
||||
io_utils_spi_close(&io_spi_dev);
|
||||
io_utils_cleanup();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
# build directories
|
||||
build
|
|
@ -1,28 +0,0 @@
|
|||
cmake_minimum_required(VERSION 3.15)
|
||||
project(cariboulite)
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
|
||||
# Bring the headers
|
||||
set(SUPER_DIR ${PROJECT_SOURCE_DIR}/..)
|
||||
include_directories(/.)
|
||||
include_directories(${SUPER_DIR})
|
||||
|
||||
# Source files
|
||||
set(SOURCES_LIB caribou_fpga.c)
|
||||
set(SOURCES ${SOURCES_LIB} test_caribou_fpga.c)
|
||||
set(EXTERN_LIBS
|
||||
${SUPER_DIR}/io_utils/build/libio_utils.a
|
||||
${SUPER_DIR}/caribou_programming/build/libcaribou_prog.a
|
||||
${SUPER_DIR}/zf_log/build/libzf_log.a
|
||||
-lpthread)
|
||||
#add_compile_options(-Wall -Wextra -pedantic -Werror)
|
||||
add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-missing-braces)
|
||||
|
||||
#Generate the static library from the sources
|
||||
add_library(caribou_fpga STATIC ${SOURCES_LIB})
|
||||
#add_executable(test_caribou_fpga ${SOURCES})
|
||||
#target_link_libraries(test_caribou_fpga rt pthread ${EXTERN_LIBS})
|
||||
|
||||
# Set the location for library installation -- i.e., /usr/lib in this case
|
||||
# not really necessary in this example. Use "sudo make install" to apply
|
||||
install(TARGETS caribou_fpga DESTINATION /usr/lib)
|
|
@ -1,618 +0,0 @@
|
|||
#ifndef ZF_LOG_LEVEL
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#endif
|
||||
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "FPGA"
|
||||
#include "zf_log/zf_log.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "caribou_fpga.h"
|
||||
|
||||
//--------------------------------------------------------------
|
||||
// Static Definitions
|
||||
//--------------------------------------------------------------
|
||||
#define IOC_MOD_VER 0
|
||||
#define IOC_SYS_CTRL_SYS_VERSION 1
|
||||
#define IOC_SYS_CTRL_MANU_ID 2
|
||||
#define IOC_SYS_CTRL_SYS_ERR_STAT 3
|
||||
#define IOC_SYS_CTRL_SYS_SOFT_RST 4
|
||||
#define IOC_SYS_CTRL_DEBUG_MODES 5
|
||||
#define IOC_SYS_CTRL_SYS_TX_SAMPLE_GAP 6
|
||||
|
||||
#define IOC_IO_CTRL_MODE 1
|
||||
#define IOC_IO_CTRL_DIG_PIN 2
|
||||
#define IOC_IO_CTRL_PMOD_DIR 3
|
||||
#define IOC_IO_CTRL_PMOD_VAL 4
|
||||
#define IOC_IO_CTRL_RF_PIN 5
|
||||
#define IOC_IO_CTRL_MXR_FM_PS 6
|
||||
#define IOC_IO_CTRL_MXR_FM_DATA 7
|
||||
|
||||
#define IOC_SMI_CTRL_FIFO_STATUS 1
|
||||
#define IOC_SMI_CHANNEL_SELECT 2
|
||||
#define IOC_SMI_CTRL_DIR_SELECT 3
|
||||
|
||||
//--------------------------------------------------------------
|
||||
// Internal Data-Types
|
||||
//--------------------------------------------------------------
|
||||
#pragma pack(1)
|
||||
typedef enum
|
||||
{
|
||||
caribou_fpga_rw_read = 0,
|
||||
caribou_fpga_rw_write = 1,
|
||||
} caribou_fpga_rw_en;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
caribou_fpga_mid_sys_ctrl = 0,
|
||||
caribou_fpga_mid_io_ctrl = 1,
|
||||
caribou_fpga_mid_smi_ctrl = 2,
|
||||
caribou_fpga_mid_res = 3,
|
||||
} caribou_fpga_mid_en;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t ioc : 5;
|
||||
caribou_fpga_mid_en mid : 2;
|
||||
caribou_fpga_rw_en rw : 1; // MSB
|
||||
} caribou_fpga_opcode_st;
|
||||
|
||||
|
||||
#pragma pack()
|
||||
|
||||
|
||||
#define CARIBOU_FPGA_CHECK_DEV(d,f) \
|
||||
{ \
|
||||
if ((d)==NULL) { ZF_LOGE("%s: dev is NULL", (f)); return -1;} \
|
||||
if (!(d)->initialized) {ZF_LOGE("%s: dev not initialized", (f)); return -1;} \
|
||||
}
|
||||
#define CARIBOU_FPGA_CHECK_PTR_NOT_NULL(p,f,n) {if ((p)==NULL) {ZF_LOGE("%s: %s is NULL",(f),(n)); return -1;}}
|
||||
|
||||
|
||||
//===================================================================
|
||||
void caribou_fpga_interrupt_handler (int event, int level, uint32_t tick, void *data)
|
||||
{
|
||||
//int i;
|
||||
//caribou_fpga_st* dev = (caribou_fpga_st*)data;
|
||||
|
||||
/*for (i=0; i<e; i++)
|
||||
{
|
||||
printf("u=%d t=%"PRIu64" c=%d g=%d l=%d f=%d (%d of %d)\n",
|
||||
dev, evt[i].report.timestamp, evt[i].report.chip,
|
||||
evt[i].report.gpio, evt[i].report.level,
|
||||
evt[i].report.flags, i+1, e);
|
||||
}*/
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
static int caribou_fpga_spi_transfer (caribou_fpga_st* dev, uint8_t *opcode, uint8_t *data)
|
||||
{
|
||||
uint8_t tx_buf[2] = {*opcode, *data};
|
||||
uint8_t rx_buf[2] = {0, 0};
|
||||
int ret = io_utils_spi_transmit(dev->io_spi, dev->io_spi_handle,
|
||||
tx_buf, rx_buf, 2, io_utils_spi_read_write);
|
||||
if (ret<0)
|
||||
{
|
||||
ZF_LOGE("spi transfer failed (%d)", ret);
|
||||
return -1;
|
||||
}
|
||||
// on success return 0 (the number of transmitted bytes are same as needed),
|
||||
// otherwise '1' (warning / error)
|
||||
|
||||
*data = rx_buf[1];
|
||||
return !(ret == sizeof(rx_buf));
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
int caribou_fpga_init(caribou_fpga_st* dev, io_utils_spi_st* io_spi)
|
||||
{
|
||||
if (dev == NULL)
|
||||
{
|
||||
ZF_LOGE("dev is NULL");
|
||||
return -1;
|
||||
}
|
||||
|
||||
dev->io_spi = io_spi;
|
||||
|
||||
ZF_LOGD("configuring reset and irq pins");
|
||||
// Configure GPIO pins
|
||||
io_utils_setup_gpio(dev->reset_pin, io_utils_dir_output, io_utils_pull_up);
|
||||
io_utils_setup_gpio(dev->soft_reset_pin, io_utils_dir_output, io_utils_pull_up);
|
||||
|
||||
// set to known state
|
||||
io_utils_write_gpio(dev->soft_reset_pin, 1);
|
||||
|
||||
ZF_LOGD("Initializing io_utils_spi");
|
||||
io_utils_hard_spi_st hard_dev_fpga = { .spi_dev_id = dev->spi_dev,
|
||||
.spi_dev_channel = dev->spi_channel, };
|
||||
dev->io_spi_handle = io_utils_spi_add_chip(dev->io_spi, dev->cs_pin, 1000000, 0, 0,
|
||||
io_utils_spi_chip_type_fpga_comm,
|
||||
&hard_dev_fpga);
|
||||
|
||||
// Init FPGA programming
|
||||
if (caribou_prog_init(&dev->prog_dev, dev->io_spi) < 0)
|
||||
{
|
||||
ZF_LOGE("ice40 programmer init failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
dev->initialized = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
int caribou_fpga_get_status(caribou_fpga_st* dev, caribou_fpga_status_en *stat)
|
||||
{
|
||||
int retries = 3;
|
||||
int inner_stat = caribou_fpga_status_not_programmed;
|
||||
|
||||
while (retries-- && inner_stat != caribou_fpga_status_operational)
|
||||
{
|
||||
caribou_fpga_get_versions (dev, NULL);
|
||||
|
||||
if (dev->versions.sys_manu_id != CARIBOU_SDR_MANU_CODE)
|
||||
{
|
||||
inner_stat = caribou_fpga_status_not_programmed;
|
||||
//caribou_fpga_print_versions (dev);
|
||||
caribou_fpga_soft_reset(dev);
|
||||
io_utils_usleep(20000);
|
||||
}
|
||||
else
|
||||
{
|
||||
inner_stat = caribou_fpga_status_operational;
|
||||
}
|
||||
}
|
||||
dev->status = inner_stat;
|
||||
if (stat) *stat = dev->status;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
int caribou_fpga_program_to_fpga(caribou_fpga_st* dev, unsigned char *buffer, size_t len, bool force_prog)
|
||||
{
|
||||
int prog_retries = 3;
|
||||
caribou_fpga_get_status(dev, NULL);
|
||||
if (dev->status == caribou_fpga_status_not_programmed || force_prog)
|
||||
{
|
||||
if (buffer == NULL || len == 0)
|
||||
{
|
||||
ZF_LOGE("buffer should be not NULL and len > 0");
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (prog_retries--)
|
||||
{
|
||||
if (caribou_prog_configure_from_buffer(&dev->prog_dev, buffer, len) != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//caribou_fpga_soft_reset(dev);
|
||||
io_utils_usleep(100000);
|
||||
|
||||
caribou_fpga_get_status(dev, NULL);
|
||||
if (dev->status == caribou_fpga_status_operational)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (prog_retries == 0)
|
||||
{
|
||||
ZF_LOGE("Programming failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ZF_LOGI("FPGA already operational - not programming (use 'force_prog=true' to force update)");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
int caribou_fpga_program_to_fpga_from_file(caribou_fpga_st* dev, char *filename, bool force_prog)
|
||||
{
|
||||
caribou_fpga_get_status(dev, NULL);
|
||||
if (dev->status == caribou_fpga_status_not_programmed || force_prog)
|
||||
{
|
||||
if (caribou_prog_configure(&dev->prog_dev, filename) < 0)
|
||||
{
|
||||
ZF_LOGE("Programming failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
caribou_fpga_soft_reset(dev);
|
||||
io_utils_usleep(100000);
|
||||
|
||||
caribou_fpga_get_status(dev, NULL);
|
||||
if (dev->status == caribou_fpga_status_not_programmed)
|
||||
{
|
||||
ZF_LOGE("Programming failed");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ZF_LOGI("FPGA already operational - not programming (use 'force_prog=true' to force update)");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
int caribou_fpga_close(caribou_fpga_st* dev)
|
||||
{
|
||||
CARIBOU_FPGA_CHECK_DEV(dev,"caribou_fpga_close");
|
||||
dev->initialized = 0;
|
||||
io_utils_spi_remove_chip(dev->io_spi, dev->io_spi_handle);
|
||||
|
||||
return caribou_prog_release(&dev->prog_dev);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
int caribou_fpga_soft_reset(caribou_fpga_st* dev)
|
||||
{
|
||||
CARIBOU_FPGA_CHECK_DEV(dev,"caribou_fpga_soft_reset");
|
||||
|
||||
io_utils_write_gpio_with_wait(dev->soft_reset_pin, 0, 1000);
|
||||
io_utils_usleep(1000);
|
||||
io_utils_write_gpio_with_wait(dev->soft_reset_pin, 1, 1000);
|
||||
io_utils_usleep(20000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
int caribou_fpga_hard_reset(caribou_fpga_st* dev)
|
||||
{
|
||||
CARIBOU_FPGA_CHECK_DEV(dev,"caribou_fpga_hard_reset (disposing firmware)");
|
||||
io_utils_write_gpio_with_wait(dev->reset_pin, 0, 1000);
|
||||
io_utils_write_gpio_with_wait(dev->reset_pin, 1, 1000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
int caribou_fpga_hard_reset_keep(caribou_fpga_st* dev, bool reset)
|
||||
{
|
||||
if (reset)
|
||||
{
|
||||
io_utils_write_gpio_with_wait(dev->reset_pin, 0, 1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
io_utils_write_gpio_with_wait(dev->reset_pin, 1, 1000);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
// System Controller
|
||||
void caribou_fpga_print_versions (caribou_fpga_st* dev)
|
||||
{
|
||||
printf("FPGA Versions:\n");
|
||||
printf(" System Version: %02X\n", dev->versions.sys_ver);
|
||||
printf(" Manu. ID: %02X\n", dev->versions.sys_manu_id);
|
||||
printf(" Sys. Ctrl Version: %02X\n", dev->versions.sys_ctrl_mod_ver);
|
||||
printf(" IO Ctrl Version: %02X\n", dev->versions.io_ctrl_mod_ver);
|
||||
printf(" SMI Ctrl Version: %02X\n", dev->versions.smi_ctrl_mod_ver);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
int caribou_fpga_get_versions (caribou_fpga_st* dev, caribou_fpga_versions_st* vers)
|
||||
{
|
||||
caribou_fpga_opcode_st oc =
|
||||
{
|
||||
.rw = caribou_fpga_rw_read,
|
||||
.mid = caribou_fpga_mid_sys_ctrl,
|
||||
};
|
||||
|
||||
uint8_t *poc = (uint8_t*)&oc;
|
||||
CARIBOU_FPGA_CHECK_DEV(dev,"caribou_fpga_get_versions");
|
||||
|
||||
oc.ioc = IOC_SYS_CTRL_SYS_VERSION;
|
||||
caribou_fpga_spi_transfer (dev, poc, &dev->versions.sys_ver);
|
||||
|
||||
oc.ioc = IOC_SYS_CTRL_MANU_ID;
|
||||
caribou_fpga_spi_transfer (dev, poc, &dev->versions.sys_manu_id);
|
||||
|
||||
oc.ioc = IOC_MOD_VER;
|
||||
oc.mid = caribou_fpga_mid_sys_ctrl;
|
||||
caribou_fpga_spi_transfer (dev, poc, &dev->versions.sys_ctrl_mod_ver);
|
||||
|
||||
oc.mid = caribou_fpga_mid_io_ctrl;
|
||||
caribou_fpga_spi_transfer (dev, poc, &dev->versions.io_ctrl_mod_ver);
|
||||
|
||||
oc.mid = caribou_fpga_mid_smi_ctrl;
|
||||
caribou_fpga_spi_transfer (dev, poc, &dev->versions.smi_ctrl_mod_ver);
|
||||
|
||||
//caribou_fpga_print_versions (dev);
|
||||
|
||||
if (vers)
|
||||
{
|
||||
memcpy (vers, &dev->versions, sizeof(caribou_fpga_versions_st));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------
|
||||
static char caribou_fpga_mode_names[][64] =
|
||||
{
|
||||
"Low Power (0)",
|
||||
"RX / TX bypass (1)",
|
||||
"RX lowpass (up-conversion) (2)",
|
||||
"RX hipass (down-conversion) (3)",
|
||||
"TX lowpass (down-conversion) (4)",
|
||||
"RX hipass (up-conversion) (5)",
|
||||
};
|
||||
|
||||
char* caribou_fpga_get_mode_name (caribou_fpga_io_ctrl_rfm_en mode)
|
||||
{
|
||||
if (mode >= caribou_fpga_io_ctrl_rfm_low_power && mode <= caribou_fpga_io_ctrl_rfm_tx_hipass)
|
||||
return caribou_fpga_mode_names[mode];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
int caribou_fpga_set_debug_modes (caribou_fpga_st* dev, bool dbg_fifo_push, bool dbg_fifo_pull, bool dbg_smi)
|
||||
{
|
||||
CARIBOU_FPGA_CHECK_DEV(dev,"caribou_fpga_set_debug_modes");
|
||||
caribou_fpga_opcode_st oc =
|
||||
{
|
||||
.rw = caribou_fpga_rw_write,
|
||||
.mid = caribou_fpga_mid_sys_ctrl,
|
||||
.ioc = IOC_SYS_CTRL_DEBUG_MODES
|
||||
};
|
||||
uint8_t mode = ((dbg_fifo_push & 0x1) << 0) | ((dbg_fifo_pull & 0x1) << 1) | ((dbg_smi & 0x1) << 2 );
|
||||
return caribou_fpga_spi_transfer (dev, (uint8_t*)(&oc), &mode);
|
||||
}
|
||||
//--------------------------------------------------------------
|
||||
int caribou_fpga_get_errors (caribou_fpga_st* dev, uint8_t *err_map)
|
||||
{
|
||||
CARIBOU_FPGA_CHECK_DEV(dev,"caribou_fpga_get_errors");
|
||||
CARIBOU_FPGA_CHECK_PTR_NOT_NULL(err_map,"caribou_fpga_get_errors","err_map");
|
||||
caribou_fpga_opcode_st oc =
|
||||
{
|
||||
.rw = caribou_fpga_rw_read,
|
||||
.mid = caribou_fpga_mid_sys_ctrl,
|
||||
.ioc = IOC_SYS_CTRL_SYS_ERR_STAT
|
||||
};
|
||||
|
||||
*err_map = 0;
|
||||
return caribou_fpga_spi_transfer (dev, (uint8_t*)(&oc), err_map);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
int caribou_fpga_set_sys_ctrl_tx_sample_gap (caribou_fpga_st* dev, uint8_t gap)
|
||||
{
|
||||
CARIBOU_FPGA_CHECK_DEV(dev,"caribou_fpga_set_sys_ctrl_tx_sample_gap");
|
||||
caribou_fpga_opcode_st oc =
|
||||
{
|
||||
.rw = caribou_fpga_rw_write,
|
||||
.mid = caribou_fpga_mid_sys_ctrl,
|
||||
.ioc = IOC_SYS_CTRL_SYS_TX_SAMPLE_GAP,
|
||||
};
|
||||
return caribou_fpga_spi_transfer (dev, (uint8_t*)(&oc), &gap);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
int caribou_fpga_get_sys_ctrl_tx_sample_gap (caribou_fpga_st* dev, uint8_t *gap)
|
||||
{
|
||||
CARIBOU_FPGA_CHECK_DEV(dev,"caribou_fpga_get_sys_ctrl_tx_sample_gap");
|
||||
|
||||
caribou_fpga_opcode_st oc =
|
||||
{
|
||||
.rw = caribou_fpga_rw_read,
|
||||
.mid = caribou_fpga_mid_sys_ctrl,
|
||||
.ioc = IOC_SYS_CTRL_SYS_TX_SAMPLE_GAP
|
||||
};
|
||||
|
||||
*gap = 0;
|
||||
return caribou_fpga_spi_transfer (dev, (uint8_t*)(&oc), gap);
|
||||
}
|
||||
|
||||
|
||||
// I/O Controller
|
||||
int caribou_fpga_set_io_ctrl_mode (caribou_fpga_st* dev, uint8_t debug_mode, caribou_fpga_io_ctrl_rfm_en rfm)
|
||||
{
|
||||
CARIBOU_FPGA_CHECK_DEV(dev,"caribou_fpga_set_io_ctrl_mode");
|
||||
|
||||
caribou_fpga_opcode_st oc =
|
||||
{
|
||||
.rw = caribou_fpga_rw_write,
|
||||
.mid = caribou_fpga_mid_io_ctrl,
|
||||
.ioc = IOC_IO_CTRL_MODE
|
||||
};
|
||||
|
||||
uint8_t mode = (debug_mode << 0) | (rfm&0x7)<<2;
|
||||
return caribou_fpga_spi_transfer (dev, (uint8_t*)(&oc), &mode);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
int caribou_fpga_get_io_ctrl_mode (caribou_fpga_st* dev, uint8_t* debug_mode, caribou_fpga_io_ctrl_rfm_en* rfm)
|
||||
{
|
||||
CARIBOU_FPGA_CHECK_DEV(dev,"caribou_fpga_get_io_ctrl_mode");
|
||||
|
||||
caribou_fpga_opcode_st oc =
|
||||
{
|
||||
.rw = caribou_fpga_rw_read,
|
||||
.mid = caribou_fpga_mid_io_ctrl,
|
||||
.ioc = IOC_IO_CTRL_MODE
|
||||
};
|
||||
|
||||
uint8_t mode = 0;
|
||||
caribou_fpga_spi_transfer (dev, (uint8_t*)(&oc), &mode);
|
||||
|
||||
*debug_mode = mode & 0x3;
|
||||
*rfm = (mode >> 2) & 0x7;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
int caribou_fpga_set_io_ctrl_dig (caribou_fpga_st* dev, int led0, int led1)
|
||||
{
|
||||
CARIBOU_FPGA_CHECK_DEV(dev,"caribou_fpga_set_io_ctrl_dig");
|
||||
|
||||
caribou_fpga_opcode_st oc =
|
||||
{
|
||||
.rw = caribou_fpga_rw_write,
|
||||
.mid = caribou_fpga_mid_io_ctrl,
|
||||
.ioc = IOC_IO_CTRL_DIG_PIN
|
||||
};
|
||||
|
||||
uint8_t pins = led1<<1 | led0<<0;
|
||||
return caribou_fpga_spi_transfer (dev, (uint8_t*)(&oc), &pins);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
int caribou_fpga_get_io_ctrl_dig (caribou_fpga_st* dev, int* led0, int* led1, int* btn, int* cfg)
|
||||
{
|
||||
CARIBOU_FPGA_CHECK_DEV(dev,"caribou_fpga_get_io_ctrl_dig");
|
||||
|
||||
caribou_fpga_opcode_st oc =
|
||||
{
|
||||
.rw = caribou_fpga_rw_read,
|
||||
.mid = caribou_fpga_mid_io_ctrl,
|
||||
.ioc = IOC_IO_CTRL_DIG_PIN
|
||||
};
|
||||
|
||||
uint8_t pins = 0;
|
||||
caribou_fpga_spi_transfer (dev, (uint8_t*)(&oc), &pins);
|
||||
if (led0) *led0 = (pins>>0)&0x01;
|
||||
if (led1) *led1 = (pins>>1)&0x01;
|
||||
if (cfg) *cfg = (pins>>3)&0x0F;
|
||||
if (btn) *btn = (pins>>7)&0x01;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
int caribou_fpga_set_io_ctrl_pmod_dir (caribou_fpga_st* dev, uint8_t dir)
|
||||
{
|
||||
CARIBOU_FPGA_CHECK_DEV(dev,"caribou_fpga_set_io_ctrl_pmod_dir");
|
||||
caribou_fpga_opcode_st oc =
|
||||
{
|
||||
.rw = caribou_fpga_rw_write,
|
||||
.mid = caribou_fpga_mid_io_ctrl,
|
||||
.ioc = IOC_IO_CTRL_PMOD_DIR
|
||||
};
|
||||
return caribou_fpga_spi_transfer (dev, (uint8_t*)(&oc), &dir);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
int caribou_fpga_get_io_ctrl_pmod_dir (caribou_fpga_st* dev, uint8_t *dir)
|
||||
{
|
||||
CARIBOU_FPGA_CHECK_DEV(dev,"caribou_fpga_get_io_ctrl_pmod_dir");
|
||||
CARIBOU_FPGA_CHECK_PTR_NOT_NULL(dir,"caribou_fpga_get_io_ctrl_pmod_dir","dir");
|
||||
caribou_fpga_opcode_st oc =
|
||||
{
|
||||
.rw = caribou_fpga_rw_read,
|
||||
.mid = caribou_fpga_mid_io_ctrl,
|
||||
.ioc = IOC_IO_CTRL_PMOD_DIR
|
||||
};
|
||||
*dir = 0;
|
||||
return caribou_fpga_spi_transfer (dev, (uint8_t*)(&oc), dir);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
int caribou_fpga_set_io_ctrl_pmod_val (caribou_fpga_st* dev, uint8_t val)
|
||||
{
|
||||
CARIBOU_FPGA_CHECK_DEV(dev,"caribou_fpga_set_io_ctrl_pmod_val");
|
||||
caribou_fpga_opcode_st oc =
|
||||
{
|
||||
.rw = caribou_fpga_rw_write,
|
||||
.mid = caribou_fpga_mid_io_ctrl,
|
||||
.ioc = IOC_IO_CTRL_PMOD_VAL
|
||||
};
|
||||
return caribou_fpga_spi_transfer (dev, (uint8_t*)(&oc), &val);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
int caribou_fpga_get_io_ctrl_pmod_val (caribou_fpga_st* dev, uint8_t *val)
|
||||
{
|
||||
CARIBOU_FPGA_CHECK_DEV(dev,"caribou_fpga_get_io_ctrl_pmod_val");
|
||||
CARIBOU_FPGA_CHECK_PTR_NOT_NULL(val,"caribou_fpga_get_io_ctrl_pmod_val","val");
|
||||
caribou_fpga_opcode_st oc =
|
||||
{
|
||||
.rw = caribou_fpga_rw_read,
|
||||
.mid = caribou_fpga_mid_io_ctrl,
|
||||
.ioc = IOC_IO_CTRL_PMOD_VAL
|
||||
};
|
||||
*val = 0;
|
||||
return caribou_fpga_spi_transfer (dev, (uint8_t*)(&oc), val);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
int caribou_fpga_set_io_ctrl_rf_state (caribou_fpga_st* dev, caribou_fpga_rf_pin_st *pins)
|
||||
{
|
||||
CARIBOU_FPGA_CHECK_DEV(dev,"caribou_fpga_set_io_ctrl_rf_state");
|
||||
caribou_fpga_opcode_st oc =
|
||||
{
|
||||
.rw = caribou_fpga_rw_write,
|
||||
.mid = caribou_fpga_mid_io_ctrl,
|
||||
.ioc = IOC_IO_CTRL_RF_PIN
|
||||
};
|
||||
return caribou_fpga_spi_transfer (dev, (uint8_t*)(&oc), (uint8_t*)pins);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
int caribou_fpga_get_io_ctrl_rf_state (caribou_fpga_st* dev, caribou_fpga_rf_pin_st *pins)
|
||||
{
|
||||
CARIBOU_FPGA_CHECK_DEV(dev,"caribou_fpga_get_io_ctrl_rf_state");
|
||||
CARIBOU_FPGA_CHECK_PTR_NOT_NULL(pins,"caribou_fpga_get_io_ctrl_rf_state","pins");
|
||||
caribou_fpga_opcode_st oc =
|
||||
{
|
||||
.rw = caribou_fpga_rw_read,
|
||||
.mid = caribou_fpga_mid_io_ctrl,
|
||||
.ioc = IOC_IO_CTRL_RF_PIN
|
||||
};
|
||||
memset(pins, 0, sizeof(caribou_fpga_rf_pin_st));
|
||||
return caribou_fpga_spi_transfer (dev, (uint8_t*)(&oc), (uint8_t*)pins);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
int caribou_fpga_get_smi_ctrl_fifo_status (caribou_fpga_st* dev, caribou_fpga_smi_fifo_status_st *status)
|
||||
{
|
||||
CARIBOU_FPGA_CHECK_DEV(dev,"caribou_fpga_get_smi_ctrl_fifo_status");
|
||||
CARIBOU_FPGA_CHECK_PTR_NOT_NULL(status,"caribou_fpga_get_smi_ctrl_fifo_status","status");
|
||||
caribou_fpga_opcode_st oc =
|
||||
{
|
||||
.rw = caribou_fpga_rw_read,
|
||||
.mid = caribou_fpga_mid_smi_ctrl,
|
||||
.ioc = IOC_SMI_CTRL_FIFO_STATUS
|
||||
};
|
||||
memset(status, 0, sizeof(caribou_fpga_smi_fifo_status_st));
|
||||
return caribou_fpga_spi_transfer (dev, (uint8_t*)(&oc), (uint8_t*)status);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
int caribou_fpga_set_smi_channel (caribou_fpga_st* dev, caribou_fpga_smi_channel_en channel)
|
||||
{
|
||||
uint8_t val = 0;
|
||||
CARIBOU_FPGA_CHECK_DEV(dev,"caribou_fpga_set_smi_channel");
|
||||
|
||||
caribou_fpga_opcode_st oc =
|
||||
{
|
||||
.rw = caribou_fpga_rw_write,
|
||||
.mid = caribou_fpga_mid_smi_ctrl,
|
||||
.ioc = IOC_SMI_CHANNEL_SELECT
|
||||
};
|
||||
val = (channel == caribou_fpga_smi_channel_0) ? 0x0 : 0x1;
|
||||
|
||||
return caribou_fpga_spi_transfer (dev, (uint8_t*)(&oc), &val);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------
|
||||
int caribou_fpga_set_smi_ctrl_data_direction (caribou_fpga_st* dev, uint8_t dir)
|
||||
{
|
||||
CARIBOU_FPGA_CHECK_DEV(dev,"caribou_fpga_set_smi_ctrl_data_direction");
|
||||
caribou_fpga_opcode_st oc =
|
||||
{
|
||||
.rw = caribou_fpga_rw_write,
|
||||
.mid = caribou_fpga_mid_smi_ctrl,
|
||||
.ioc = IOC_SMI_CTRL_DIR_SELECT
|
||||
};
|
||||
return caribou_fpga_spi_transfer (dev, (uint8_t*)(&oc), (uint8_t*)&dir);
|
||||
}
|
|
@ -1,181 +0,0 @@
|
|||
#ifndef __CARIBOU_FPGA_H__
|
||||
#define __CARIBOU_FPGA_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include "io_utils/io_utils.h"
|
||||
#include "io_utils/io_utils_spi.h"
|
||||
#include "caribou_programming/caribou_prog.h"
|
||||
|
||||
/**
|
||||
* @brief Tha manufacturer code - used to verify the FPGA's programming
|
||||
*/
|
||||
#define CARIBOU_SDR_MANU_CODE 0x1
|
||||
|
||||
#pragma pack(1)
|
||||
/**
|
||||
* @brief Firmware versions and inner modules information
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint8_t sys_ver;
|
||||
uint8_t sys_manu_id;
|
||||
uint8_t sys_ctrl_mod_ver;
|
||||
uint8_t io_ctrl_mod_ver;
|
||||
uint8_t smi_ctrl_mod_ver;
|
||||
} caribou_fpga_versions_st;
|
||||
|
||||
/**
|
||||
* @brief Firmware interface generic error codes
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
caribou_fpga_ec_okay = 0x00,
|
||||
caribou_fpga_ec_write_attempt_to_readonly = 0x01,
|
||||
} caribou_fpga_ec_en; // error codes
|
||||
|
||||
/**
|
||||
* @brief RF front end modes of operations
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
caribou_fpga_io_ctrl_rfm_low_power = 0,
|
||||
caribou_fpga_io_ctrl_rfm_bypass = 1,
|
||||
caribou_fpga_io_ctrl_rfm_rx_lowpass = 2,
|
||||
caribou_fpga_io_ctrl_rfm_rx_hipass = 3,
|
||||
caribou_fpga_io_ctrl_rfm_tx_lowpass = 4,
|
||||
caribou_fpga_io_ctrl_rfm_tx_hipass = 5,
|
||||
} caribou_fpga_io_ctrl_rfm_en;
|
||||
|
||||
/**
|
||||
* @brief FPGA status - either not programmed or programmed with
|
||||
* a valid firmware
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
caribou_fpga_status_not_programmed = 0,
|
||||
caribou_fpga_status_operational = 1,
|
||||
} caribou_fpga_status_en;
|
||||
|
||||
/**
|
||||
* @brief RFFE controlling digital pins specifically controlled
|
||||
* when in debug mode (read anytime)
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint8_t mixer_en : 1; // LSB
|
||||
uint8_t lna_rx_shdn : 1;
|
||||
uint8_t lna_tx_shdn : 1;
|
||||
uint8_t trvc2 : 1;
|
||||
uint8_t trvc1_b : 1;
|
||||
uint8_t trvc1 : 1;
|
||||
uint8_t rx_h_tx_l_b : 1;
|
||||
uint8_t rx_h_tx_l : 1; // MSB
|
||||
} caribou_fpga_rf_pin_st;
|
||||
|
||||
/**
|
||||
* @brief SMI fifo status struct
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
uint8_t rx_fifo_empty : 1; // LSB
|
||||
uint8_t tx_fifo_full : 1;
|
||||
uint8_t smi_channel: 1;
|
||||
uint8_t i_smi_test : 1;
|
||||
uint8_t reserved : 4; // MSB
|
||||
} caribou_fpga_smi_fifo_status_st;
|
||||
|
||||
/**
|
||||
* @brief SMI channel select
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
caribou_fpga_smi_channel_0 = 0,
|
||||
caribou_fpga_smi_channel_1 = 1,
|
||||
} caribou_fpga_smi_channel_en;
|
||||
|
||||
#pragma pack()
|
||||
|
||||
/**
|
||||
* @brief Firmware control and programming device context
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
// pinout
|
||||
int reset_pin;
|
||||
int soft_reset_pin;
|
||||
int cs_pin;
|
||||
|
||||
// spi device
|
||||
int spi_dev;
|
||||
int spi_channel;
|
||||
|
||||
// programming
|
||||
caribou_prog_st prog_dev;
|
||||
caribou_fpga_status_en status;
|
||||
caribou_fpga_versions_st versions;
|
||||
|
||||
// internal controls
|
||||
io_utils_spi_st* io_spi;
|
||||
int io_spi_handle;
|
||||
int initialized;
|
||||
} caribou_fpga_st;
|
||||
|
||||
/**
|
||||
* @brief initialize FPGA device driver
|
||||
*
|
||||
* @param dev pointer to device context - should be preinitialized with pinout and spi info
|
||||
* @param io_spi spi device
|
||||
* @return int success (0) / failure (-1)
|
||||
*/
|
||||
int caribou_fpga_init(caribou_fpga_st* dev, io_utils_spi_st* io_spi);
|
||||
|
||||
int caribou_fpga_close(caribou_fpga_st* dev);
|
||||
int caribou_fpga_soft_reset(caribou_fpga_st* dev);
|
||||
int caribou_fpga_hard_reset(caribou_fpga_st* dev);
|
||||
int caribou_fpga_hard_reset_keep(caribou_fpga_st* dev, bool reset);
|
||||
|
||||
// programming
|
||||
int caribou_fpga_get_status(caribou_fpga_st* dev, caribou_fpga_status_en *stat);
|
||||
int caribou_fpga_program_to_fpga(caribou_fpga_st* dev, unsigned char *buffer, size_t len, bool force_prog);
|
||||
int caribou_fpga_program_to_fpga_from_file(caribou_fpga_st* dev, char *filename, bool force_prog);
|
||||
|
||||
// System Controller
|
||||
int caribou_fpga_get_versions (caribou_fpga_st* dev, caribou_fpga_versions_st *vers);
|
||||
void caribou_fpga_print_versions (caribou_fpga_st* dev);
|
||||
int caribou_fpga_get_errors (caribou_fpga_st* dev, uint8_t *err_map);
|
||||
char* caribou_fpga_get_mode_name (caribou_fpga_io_ctrl_rfm_en mode);
|
||||
int caribou_fpga_set_debug_modes (caribou_fpga_st* dev, bool dbg_fifo_push, bool dbg_fifo_pull, bool dbg_smi);
|
||||
|
||||
int caribou_fpga_set_sys_ctrl_tx_sample_gap (caribou_fpga_st* dev, uint8_t gap);
|
||||
int caribou_fpga_get_sys_ctrl_tx_sample_gap (caribou_fpga_st* dev, uint8_t *gap);
|
||||
// I/O Controller
|
||||
int caribou_fpga_set_io_ctrl_mode (caribou_fpga_st* dev, uint8_t debug_mode, caribou_fpga_io_ctrl_rfm_en rfm);
|
||||
int caribou_fpga_get_io_ctrl_mode (caribou_fpga_st* dev, uint8_t *debug_mode, caribou_fpga_io_ctrl_rfm_en *rfm);
|
||||
|
||||
int caribou_fpga_set_io_ctrl_dig (caribou_fpga_st* dev, int led0, int led1);
|
||||
int caribou_fpga_get_io_ctrl_dig (caribou_fpga_st* dev, int *led0, int *led1, int *btn, int *cfg);
|
||||
|
||||
int caribou_fpga_set_io_ctrl_pmod_dir (caribou_fpga_st* dev, uint8_t dir);
|
||||
int caribou_fpga_get_io_ctrl_pmod_dir (caribou_fpga_st* dev, uint8_t *dir);
|
||||
|
||||
int caribou_fpga_set_io_ctrl_pmod_val (caribou_fpga_st* dev, uint8_t val);
|
||||
int caribou_fpga_get_io_ctrl_pmod_val (caribou_fpga_st* dev, uint8_t *val);
|
||||
|
||||
int caribou_fpga_set_io_ctrl_rf_state (caribou_fpga_st* dev, caribou_fpga_rf_pin_st *pins);
|
||||
int caribou_fpga_get_io_ctrl_rf_state (caribou_fpga_st* dev, caribou_fpga_rf_pin_st *pins);
|
||||
|
||||
// SMI Controller
|
||||
int caribou_fpga_get_smi_ctrl_fifo_status (caribou_fpga_st* dev, caribou_fpga_smi_fifo_status_st *status);
|
||||
int caribou_fpga_set_smi_channel (caribou_fpga_st* dev, caribou_fpga_smi_channel_en channel);
|
||||
int caribou_fpga_set_smi_ctrl_data_direction (caribou_fpga_st* dev, uint8_t dir);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __CARIBOU_FPGA_H__
|
|
@ -1,220 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include "caribou_fpga.h"
|
||||
|
||||
#define CARIBOULITE_MOSI 20
|
||||
#define CARIBOULITE_SCK 21
|
||||
#define CARIBOULITE_MISO 19
|
||||
|
||||
#define FPGA_RESET 26
|
||||
#define FPGA_CS 18
|
||||
|
||||
#define CARIBOULITE_SPI_DEV 1
|
||||
#define CARIBOULITE_FPGA_SPI_CHANNEL 0
|
||||
|
||||
io_utils_spi_st io_spi_dev =
|
||||
{
|
||||
.miso = CARIBOULITE_MISO,
|
||||
.mosi = CARIBOULITE_MOSI,
|
||||
.sck = CARIBOULITE_SCK,
|
||||
};
|
||||
|
||||
caribou_fpga_st dev =
|
||||
{
|
||||
.reset_pin = FPGA_RESET,
|
||||
.cs_pin = FPGA_CS,
|
||||
.spi_dev = CARIBOULITE_SPI_DEV,
|
||||
.spi_channel = CARIBOULITE_FPGA_SPI_CHANNEL,
|
||||
};
|
||||
|
||||
int main ()
|
||||
{
|
||||
printf("Hello from CaribouFPGA!\n");
|
||||
|
||||
// Init GPIOs and set FPGA on reset
|
||||
io_utils_setup(NULL);
|
||||
|
||||
// Init spi
|
||||
io_utils_spi_init(&io_spi_dev);
|
||||
|
||||
// init fpga comm
|
||||
caribou_fpga_init(&dev, &io_spi_dev);
|
||||
|
||||
// get versions
|
||||
caribou_fpga_versions_st versions = {0};
|
||||
caribou_fpga_get_versions (&dev, &versions);
|
||||
printf("VERSIONS: sys: 0x%02X, manu: 0x%02X, sys_ctrl: 0x%02X, io_ctrl: 0x%02X, smi_ctrl: 0x%02X\n",
|
||||
versions.sys_ver,
|
||||
versions.sys_manu_id,
|
||||
versions.sys_ctrl_mod_ver,
|
||||
versions.io_ctrl_mod_ver,
|
||||
versions.smi_ctrl_mod_ver);
|
||||
|
||||
// get errors
|
||||
uint8_t err_map = 0;
|
||||
caribou_fpga_get_errors (&dev, &err_map);
|
||||
printf("ERRORS: 0x%02X\n -- Press Enter to Soft-Reset --\n", err_map);
|
||||
|
||||
// soft reset
|
||||
getchar();
|
||||
caribou_fpga_soft_reset(&dev);
|
||||
|
||||
// io control mode
|
||||
uint8_t debug_mode = 0;
|
||||
caribou_fpga_io_ctrl_rfm_en rfmode = 0;
|
||||
caribou_fpga_get_io_ctrl_mode (&dev, &debug_mode, &rfmode);
|
||||
printf("IO_CTRL MODE: debug = %d, rfm = %d\n", debug_mode, rfmode);
|
||||
|
||||
// io_ctrl_dig
|
||||
int led0 = 0;
|
||||
int led1 = 0;
|
||||
int btn = 0;
|
||||
int cfg = 0;
|
||||
|
||||
caribou_fpga_get_io_ctrl_dig (&dev, &led0, &led1, &btn, &cfg);
|
||||
printf("IO_CTRL: led0: %d, led1: %d, btn: %d, cfg: 0x%02X\n", led0, led1, btn, cfg);
|
||||
|
||||
// pmod dir
|
||||
uint8_t dir = 0;
|
||||
caribou_fpga_get_io_ctrl_pmod_dir (&dev, &dir);
|
||||
printf("PMOD_DIR: dir = 0x%02X\n", dir);
|
||||
|
||||
// pmod val
|
||||
uint8_t val = 0;
|
||||
caribou_fpga_get_io_ctrl_pmod_val (&dev, &val);
|
||||
printf("PMOD_VAL: val = 0x%02X\n", val);
|
||||
|
||||
// rf state
|
||||
caribou_fpga_rf_pin_st pins = {0};
|
||||
uint32_t *pins_uint = (uint32_t*)&pins;
|
||||
caribou_fpga_get_io_ctrl_rf_state (&dev, &pins);
|
||||
printf("RF_PIN_STATE: val = 0x%02X\n", *(pins_uint));
|
||||
|
||||
// smi fifo status
|
||||
caribou_fpga_smi_fifo_status_st fifo_stat = {0};
|
||||
caribou_fpga_get_smi_ctrl_fifo_status (&dev, &fifo_stat);
|
||||
printf("SMI_FIFO_STAT: rx_09_empty = %d, rx_09_full = %d, rx_24_empty = %d, rx_24_full = %d, res = 0x%02X\n",
|
||||
fifo_stat.rx_fifo_09_empty,
|
||||
fifo_stat.rx_fifo_09_full,
|
||||
fifo_stat.rx_fifo_24_empty,
|
||||
fifo_stat.rx_fifo_24_full,
|
||||
fifo_stat.res );
|
||||
|
||||
// setting RF states RFM
|
||||
printf("MODE = caribou_fpga_io_ctrl_rfm_low_power\npress enter\n");
|
||||
caribou_fpga_set_io_ctrl_mode (&dev, 0, caribou_fpga_io_ctrl_rfm_low_power);
|
||||
caribou_fpga_get_io_ctrl_mode (&dev, &debug_mode, &rfmode);
|
||||
printf("IO_CTRL MODE: debug = %d, rfm = %d (should be %d)\n", debug_mode, rfmode, caribou_fpga_io_ctrl_rfm_low_power);
|
||||
caribou_fpga_get_io_ctrl_rf_state (&dev, &pins);
|
||||
printf("RF_PIN_STATE: val = 0x%02X\n", *(pins_uint));
|
||||
|
||||
getchar();
|
||||
|
||||
printf("MODE = caribou_fpga_io_ctrl_rfm_bypass\npress enter\n");
|
||||
caribou_fpga_set_io_ctrl_mode (&dev, 0, caribou_fpga_io_ctrl_rfm_bypass);
|
||||
caribou_fpga_get_io_ctrl_mode (&dev, &debug_mode, &rfmode);
|
||||
printf("IO_CTRL MODE: debug = %d, rfm = %d (should be %d)\n", debug_mode, rfmode, caribou_fpga_io_ctrl_rfm_bypass);
|
||||
caribou_fpga_get_io_ctrl_rf_state (&dev, &pins);
|
||||
printf("RF_PIN_STATE: val = 0x%02X\n", *(pins_uint));
|
||||
|
||||
getchar();
|
||||
|
||||
printf("MODE = caribou_fpga_io_ctrl_rfm_rx_lowpass\npress enter\n");
|
||||
caribou_fpga_set_io_ctrl_mode (&dev, 0, caribou_fpga_io_ctrl_rfm_rx_lowpass);
|
||||
caribou_fpga_get_io_ctrl_mode (&dev, &debug_mode, &rfmode);
|
||||
printf("IO_CTRL MODE: debug = %d, rfm = %d (should be %d)\n", debug_mode, rfmode, caribou_fpga_io_ctrl_rfm_rx_lowpass);
|
||||
caribou_fpga_get_io_ctrl_rf_state (&dev, &pins);
|
||||
printf("RF_PIN_STATE: val = 0x%02X\n", *(pins_uint));
|
||||
|
||||
getchar();
|
||||
|
||||
printf("MODE = caribou_fpga_io_ctrl_rfm_rx_hipass\npress enter\n");
|
||||
caribou_fpga_set_io_ctrl_mode (&dev, 0, caribou_fpga_io_ctrl_rfm_rx_hipass);
|
||||
caribou_fpga_get_io_ctrl_mode (&dev, &debug_mode, &rfmode);
|
||||
printf("IO_CTRL MODE: debug = %d, rfm = %d (should be %d)\n", debug_mode, rfmode, caribou_fpga_io_ctrl_rfm_rx_hipass);
|
||||
caribou_fpga_get_io_ctrl_rf_state (&dev, &pins);
|
||||
printf("RF_PIN_STATE: val = 0x%02X\n", *(pins_uint));
|
||||
|
||||
getchar();
|
||||
|
||||
printf("MODE = caribou_fpga_io_ctrl_rfm_tx_lowpass\npress enter\n");
|
||||
caribou_fpga_set_io_ctrl_mode (&dev, 0, caribou_fpga_io_ctrl_rfm_tx_lowpass);
|
||||
caribou_fpga_get_io_ctrl_mode (&dev, &debug_mode, &rfmode);
|
||||
printf("IO_CTRL MODE: debug = %d, rfm = %d (should be %d)\n", debug_mode, rfmode, caribou_fpga_io_ctrl_rfm_tx_lowpass);
|
||||
caribou_fpga_get_io_ctrl_rf_state (&dev, &pins);
|
||||
printf("RF_PIN_STATE: val = 0x%02X\n", *(pins_uint));
|
||||
|
||||
getchar();
|
||||
|
||||
printf("MODE = caribou_fpga_io_ctrl_rfm_tx_hipass\npress enter\n");
|
||||
caribou_fpga_set_io_ctrl_mode (&dev, 0, caribou_fpga_io_ctrl_rfm_tx_hipass);
|
||||
caribou_fpga_get_io_ctrl_mode (&dev, &debug_mode, &rfmode);
|
||||
printf("IO_CTRL MODE: debug = %d, rfm = %d (should be %d)\n", debug_mode, rfmode, caribou_fpga_io_ctrl_rfm_tx_hipass);
|
||||
caribou_fpga_get_io_ctrl_rf_state (&dev, &pins);
|
||||
printf("RF_PIN_STATE: val = 0x%02X\n", *(pins_uint));
|
||||
|
||||
getchar();
|
||||
caribou_fpga_set_io_ctrl_mode (&dev, 0, caribou_fpga_io_ctrl_rfm_low_power);
|
||||
caribou_fpga_get_io_ctrl_mode (&dev, &debug_mode, &rfmode);
|
||||
printf("IO_CTRL MODE: debug = %d, rfm = %d (should be %d)\n", debug_mode, rfmode, caribou_fpga_io_ctrl_rfm_low_power);
|
||||
caribou_fpga_get_io_ctrl_rf_state (&dev, &pins);
|
||||
printf("RF_PIN_STATE: val = 0x%02X\n", *(pins_uint));
|
||||
|
||||
// read out stuff
|
||||
/*caribou_fpga_versions_st vers = {0};
|
||||
uint8_t err_map = 0;
|
||||
|
||||
int error_count = 0;
|
||||
int error_count1 = 0;
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
caribou_fpga_get_versions (&dev, &vers);
|
||||
|
||||
if (vers.sys_ver != 0x01 || vers.sys_manu_id != 0x01 ||
|
||||
vers.sys_ctrl_mod_ver != 0x01 || vers.io_ctrl_mod_ver != 0x01 ||
|
||||
vers.smi_ctrl_mod_ver != 0x01)
|
||||
error_count ++;
|
||||
|
||||
uint8_t val = 0;
|
||||
caribou_fpga_set_io_ctrl_pmod_val (&dev, i&0xFF);
|
||||
caribou_fpga_get_io_ctrl_pmod_val (&dev, &val);
|
||||
if (val != (i&0xFF))
|
||||
{
|
||||
error_count1 ++ ;
|
||||
}
|
||||
//printf("Versions: %d, %d, %d, %d, %d\n", vers.sys_ver, vers.sys_manu_id,
|
||||
// vers.sys_ctrl_mod_ver, vers.io_ctrl_mod_ver, vers.smi_ctrl_mod_ver);
|
||||
}
|
||||
|
||||
printf("Errors # = %d, error_count1 = %d\n", error_count, error_count1);
|
||||
|
||||
//caribou_fpga_get_errors (&dev, &err_map);
|
||||
//printf("Received Error map: %02X\n", err_map);
|
||||
|
||||
// settings
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
caribou_fpga_set_io_ctrl_dig (&dev, 1, 1, 1);
|
||||
caribou_fpga_set_io_ctrl_pmod_val (&dev, 0xAA);
|
||||
io_utils_usleep(500000);
|
||||
caribou_fpga_set_io_ctrl_dig (&dev, 0, 0, 0);
|
||||
caribou_fpga_set_io_ctrl_pmod_val (&dev, 0x55);
|
||||
io_utils_usleep(500000);
|
||||
}
|
||||
caribou_fpga_set_io_ctrl_dig (&dev, 1, 0, 1);
|
||||
//caribou_fpga_set_io_ctrl_pmod_dir (&dev, 0x55);
|
||||
caribou_fpga_set_io_ctrl_pmod_val (&dev, 0xAA);
|
||||
|
||||
uint8_t val = 0;
|
||||
caribou_fpga_get_io_ctrl_pmod_val (&dev, &val);
|
||||
printf("PMOD VAL: %02X\n", val);*/
|
||||
|
||||
|
||||
|
||||
// close everything
|
||||
caribou_fpga_close(&dev);
|
||||
io_utils_spi_close(&io_spi_dev);
|
||||
io_utils_cleanup();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
# build directories
|
||||
build
|
|
@ -1,21 +0,0 @@
|
|||
cmake_minimum_required(VERSION 3.15)
|
||||
project(cariboulite)
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
|
||||
#Bring the headers, such as Student.h into the project
|
||||
set(SUPER_DIR ${PROJECT_SOURCE_DIR}/..)
|
||||
include_directories(/.)
|
||||
include_directories(${SUPER_DIR})
|
||||
|
||||
#However, the file(GLOB...) allows for wildcard additions:
|
||||
set(SOURCES_LIB caribou_prog.c)
|
||||
#add_compile_options(-Wall -Wextra -pedantic -Werror)
|
||||
add_compile_options(-Wall -Wextra -Wno-missing-braces)
|
||||
|
||||
#Generate the static library from the sources
|
||||
add_library(caribou_prog STATIC ${SOURCES_LIB})
|
||||
target_include_directories(caribou_prog PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
#Set the location for library installation -- i.e., /usr/lib in this case
|
||||
# not really necessary in this example. Use "sudo make install" to apply
|
||||
install(TARGETS caribou_prog DESTINATION /usr/lib)
|
|
@ -1,5 +0,0 @@
|
|||
# Overview
|
||||
This Lattice ICE40 programming and management library was originally written by Eric Brombaugh (https://github.com/emeb) and used in https://github.com/emeb/icehat ("...RPi hat which comprises a Lattice iCE40 Ultra FPGA type ice5LP4K-SG48...").
|
||||
|
||||
# License
|
||||
<a rel="license" href="http://creativecommons.org/licenses/by/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by/4.0/88x31.png" /></a><br />This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>.
|
|
@ -1,406 +0,0 @@
|
|||
#ifndef ZF_LOG_LEVEL
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#endif
|
||||
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "CARIBOU_PROG"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include "zf_log/zf_log.h"
|
||||
#include "caribou_prog.h"
|
||||
|
||||
|
||||
#define LATTICE_ICE40_BUFSIZE 512
|
||||
#define LATTICE_ICE40_TO_COUNT 200
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
/**
|
||||
* @brief check whether the fpga is currently in a "programmed" mode
|
||||
*
|
||||
* @param dev programmer context
|
||||
* @return int success(0), error(-1)
|
||||
*/
|
||||
static int caribou_prog_check_if_programmed(caribou_prog_st* dev)
|
||||
{
|
||||
if (dev == NULL)
|
||||
{
|
||||
ZF_LOGE("device pointer NULL");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!dev->initialized)
|
||||
{
|
||||
ZF_LOGE("device not initialized");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return io_utils_read_gpio(dev->cdone_pin);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
/**
|
||||
* @brief initialize programmer context
|
||||
*
|
||||
* @param dev programmer device context
|
||||
* @param io_spi spi device wrapper
|
||||
* @return int success(0) / error(-1)
|
||||
*/
|
||||
int caribou_prog_init(caribou_prog_st *dev, io_utils_spi_st* io_spi)
|
||||
{
|
||||
if (dev == NULL)
|
||||
{
|
||||
ZF_LOGE("device pointer NULL");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (dev->initialized)
|
||||
{
|
||||
ZF_LOGE("device already initialized");
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev->io_spi = io_spi;
|
||||
|
||||
// Init pin directions
|
||||
io_utils_setup_gpio(dev->cdone_pin, io_utils_dir_input, io_utils_pull_up);
|
||||
io_utils_setup_gpio(dev->cs_pin, io_utils_dir_output, io_utils_pull_up);
|
||||
io_utils_setup_gpio(dev->reset_pin, io_utils_dir_output, io_utils_pull_up);
|
||||
|
||||
dev->io_spi_handle = io_utils_spi_add_chip( dev->io_spi,
|
||||
dev->cs_pin,
|
||||
2000000,
|
||||
0,
|
||||
0,
|
||||
io_utils_spi_chip_ice40_prog, NULL);
|
||||
|
||||
dev->initialized = 1;
|
||||
|
||||
// check if the FPGA is already configures
|
||||
if (caribou_prog_check_if_programmed(dev) == 1)
|
||||
{
|
||||
ZF_LOGD("FPGA is already configured and running");
|
||||
}
|
||||
|
||||
ZF_LOGD("device init completed");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
/**
|
||||
* @brief release the fpga programmer context
|
||||
*
|
||||
* @param dev device context
|
||||
* @return int success(0) / error(-1)
|
||||
*/
|
||||
int caribou_prog_release(caribou_prog_st *dev)
|
||||
{
|
||||
if (dev == NULL)
|
||||
{
|
||||
ZF_LOGE("device pointer NULL");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!dev->initialized)
|
||||
{
|
||||
ZF_LOGE("device not initialized");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Release the SPI device
|
||||
io_utils_spi_remove_chip(dev->io_spi, dev->io_spi_handle);
|
||||
|
||||
// Release the GPIO functions
|
||||
io_utils_setup_gpio(dev->cdone_pin, io_utils_dir_input, io_utils_pull_up);
|
||||
io_utils_setup_gpio(dev->cs_pin, io_utils_dir_input, io_utils_pull_up);
|
||||
io_utils_setup_gpio(dev->reset_pin, io_utils_dir_input, io_utils_pull_up);
|
||||
|
||||
dev->initialized = 0;
|
||||
ZF_LOGI("device release completed");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
/**
|
||||
* @brief performs preparation steps towards bitstream programming
|
||||
*
|
||||
* @param dev device context
|
||||
* @return int success(0) / error(-1)
|
||||
*/
|
||||
static int caribou_prog_configure_prepare(caribou_prog_st *dev)
|
||||
{
|
||||
long ct;
|
||||
uint8_t byte = 0xFF;
|
||||
uint8_t rxbyte = 0;
|
||||
|
||||
// set SS low for spi config
|
||||
io_utils_write_gpio_with_wait(dev->cs_pin, 0, 200);
|
||||
|
||||
// pulse RST low min 200 us ns
|
||||
io_utils_write_gpio_with_wait(dev->reset_pin, 0, 200);
|
||||
io_utils_usleep(200);
|
||||
|
||||
// Wait for DONE low
|
||||
ZF_LOGD("RESET low, Waiting for CDONE low");
|
||||
ct = LATTICE_ICE40_TO_COUNT;
|
||||
|
||||
while(io_utils_read_gpio(dev->cdone_pin)==1 && ct--)
|
||||
{
|
||||
asm volatile ("nop");
|
||||
}
|
||||
if (!ct)
|
||||
{
|
||||
ZF_LOGE("CDONE didn't fall during RESET='0'");
|
||||
return -1;
|
||||
}
|
||||
io_utils_write_gpio_with_wait(dev->reset_pin, 1, 200);
|
||||
io_utils_usleep(1200);
|
||||
|
||||
// Send 8 dummy clocks with SS high
|
||||
io_utils_write_gpio_with_wait(dev->cs_pin, 1, 200);
|
||||
io_utils_spi_transmit(dev->io_spi, dev->io_spi_handle, &byte, &rxbyte, 1, io_utils_spi_write);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
/**
|
||||
* @brief performs finalization steps after bitstream programming
|
||||
*
|
||||
* @param dev device context
|
||||
* @return int success(0) / error(-1)
|
||||
*/
|
||||
static int caribou_prog_configure_finish(caribou_prog_st *dev)
|
||||
{
|
||||
int ct = 0;
|
||||
uint8_t byte = 0xFF;
|
||||
uint8_t rxbyte = 0;
|
||||
unsigned char dummybuf[10] = {0};
|
||||
// Transmit at least 49 clock cycles of clock
|
||||
io_utils_spi_transmit(dev->io_spi, dev->io_spi_handle,
|
||||
dummybuf,
|
||||
dummybuf,
|
||||
8,
|
||||
io_utils_spi_write);
|
||||
|
||||
|
||||
/* send dummy data while waiting for DONE */
|
||||
ZF_LOGD("sending dummy clocks, waiting for CDONE to rise (or fail)");
|
||||
|
||||
ct = LATTICE_ICE40_TO_COUNT;
|
||||
int count = 0;
|
||||
while((caribou_prog_check_if_programmed(dev)==0 && ct--) || count++ < 4)
|
||||
{
|
||||
io_utils_spi_transmit(dev->io_spi, dev->io_spi_handle,
|
||||
&byte, &rxbyte, 1, io_utils_spi_write);
|
||||
}
|
||||
|
||||
if(ct)
|
||||
{
|
||||
ZF_LOGD("%d dummy clocks sent", (LATTICE_ICE40_TO_COUNT-ct)*8);
|
||||
}
|
||||
else
|
||||
{
|
||||
ZF_LOGW("timeout waiting for CDONE");
|
||||
}
|
||||
|
||||
/* return status */
|
||||
if (caribou_prog_check_if_programmed(dev)==0)
|
||||
{
|
||||
ZF_LOGE("config failed - CDONE not high");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
/**
|
||||
* @brief starts programming sequence from a memory buffer
|
||||
*
|
||||
* @param dev device context
|
||||
* @param dest the destination of the bitstream
|
||||
* @param buffer bitstream buffer pointer
|
||||
* @param buffer_size bitstream buffer length in bytes
|
||||
* @return int success(0), error (-1)
|
||||
*/
|
||||
int caribou_prog_configure_from_buffer( caribou_prog_st *dev,
|
||||
uint8_t *buffer,
|
||||
uint32_t buffer_size)
|
||||
{
|
||||
int ct = 0;
|
||||
int progress = 0;
|
||||
|
||||
if (dev == NULL)
|
||||
{
|
||||
ZF_LOGE("device pointer NULL");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!dev->initialized)
|
||||
{
|
||||
ZF_LOGE("device not initialized");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// CONFIGURATION PROLOG
|
||||
// --------------------
|
||||
if (caribou_prog_configure_prepare( dev ) != 0)
|
||||
{
|
||||
ZF_LOGE("Preparation for bitstream sending to fpga failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// CONFIGURATION
|
||||
// -------------
|
||||
// Read file & send bitstream to FPGA via SPI with CS LOW
|
||||
ZF_LOGI("Sending bitstream of size %d", buffer_size);
|
||||
ct = 0;
|
||||
io_utils_write_gpio_with_wait(dev->cs_pin, 0, 200);
|
||||
while( ct < (int)(buffer_size) )
|
||||
{
|
||||
unsigned char dummybuf[LATTICE_ICE40_BUFSIZE];
|
||||
char* readbuf = (char*)(buffer + ct);
|
||||
int length = (buffer_size-ct)<LATTICE_ICE40_BUFSIZE ? buffer_size-ct : LATTICE_ICE40_BUFSIZE;
|
||||
|
||||
// Send bitstream
|
||||
io_utils_spi_transmit(dev->io_spi, dev->io_spi_handle,
|
||||
(unsigned char *)readbuf,
|
||||
dummybuf,
|
||||
length,
|
||||
io_utils_spi_write);
|
||||
ct += length;
|
||||
|
||||
// progress
|
||||
progress = (ct * 100) / buffer_size;
|
||||
printf("[%2d%%]\r", progress); fflush(stdout);
|
||||
}
|
||||
io_utils_write_gpio_with_wait(dev->cs_pin, 1, 200);
|
||||
ZF_LOGD("bitstream sent %d bytes", ct);
|
||||
|
||||
// CONFIGURATION EPILOGUE
|
||||
// ----------------------
|
||||
if (caribou_prog_configure_finish(dev) != 0)
|
||||
{
|
||||
ZF_LOGE("Finishing the bitstream sending to fpga failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ZF_LOGI("FPGA programming - Success!\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
/**
|
||||
* @brief starts programming sequence from a binary file
|
||||
*
|
||||
* @param dev device context
|
||||
* @param dest the destination of the bitstream
|
||||
* @param bitfilename path to the file containing the fpga bitstream
|
||||
* @return int success(0), error (-1)
|
||||
*/
|
||||
int caribou_prog_configure(caribou_prog_st *dev, char *bitfilename)
|
||||
{
|
||||
FILE *fd = NULL;
|
||||
int ct = 0;
|
||||
int read;
|
||||
unsigned char dummybuf[LATTICE_ICE40_BUFSIZE];
|
||||
char readbuf[LATTICE_ICE40_BUFSIZE];
|
||||
int progress = 0;
|
||||
long file_length = 0;
|
||||
|
||||
if (dev == NULL)
|
||||
{
|
||||
ZF_LOGE("device pointer NULL");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!dev->initialized)
|
||||
{
|
||||
ZF_LOGE("device not initialized");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// FILE OPENING
|
||||
// ------------
|
||||
if(!(fd = fopen(bitfilename, "r")))
|
||||
{
|
||||
ZF_LOGE("open file %s failed", bitfilename);
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
fseek(fd, 0L, SEEK_END);
|
||||
file_length = ftell(fd);
|
||||
ZF_LOGI("opened bitstream file %s", bitfilename);
|
||||
fseek(fd, 0L, SEEK_SET);
|
||||
}
|
||||
|
||||
// CONFIGURATION PROLOG
|
||||
// --------------------
|
||||
if (caribou_prog_configure_prepare( dev ) != 0)
|
||||
{
|
||||
ZF_LOGE("Preparation for bitstream sending to fpga failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// CONFIGURATION
|
||||
// -------------
|
||||
// Read file & send bitstream to FPGA via SPI with CS LOW
|
||||
ZF_LOGI("Sending bitstream of size %ld", file_length);
|
||||
ct = 0;
|
||||
io_utils_write_gpio_with_wait(dev->cs_pin, 0, 200);
|
||||
while( (read=fread(readbuf, sizeof(char), LATTICE_ICE40_BUFSIZE, fd)) > 0 )
|
||||
{
|
||||
// Send bitstream
|
||||
io_utils_spi_transmit(dev->io_spi, dev->io_spi_handle,
|
||||
(unsigned char *)readbuf,
|
||||
dummybuf,
|
||||
read,
|
||||
io_utils_spi_write);
|
||||
ct += read;
|
||||
|
||||
// progress
|
||||
progress = (ct * 100) / file_length;
|
||||
printf("[%2d%%]\r", progress); fflush(stdout);
|
||||
}
|
||||
io_utils_write_gpio_with_wait(dev->cs_pin, 1, 200);
|
||||
|
||||
// close file
|
||||
ZF_LOGI("bitstream sent, closing file - sent %d bytes", ct);
|
||||
fclose(fd);
|
||||
|
||||
// CONFIGURATION EPILOGUE
|
||||
// ----------------------
|
||||
if (caribou_prog_configure_finish(dev) != 0)
|
||||
{
|
||||
ZF_LOGE("Finishing the bitstream sending to fpga failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ZF_LOGI("FPGA programming - Success!\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
int caribou_prog_hard_reset(caribou_prog_st *dev, int level)
|
||||
{
|
||||
if (level == 0 || level == -1)
|
||||
{
|
||||
ZF_LOGD("Resetting FPGA reset pin to 0");
|
||||
io_utils_write_gpio_with_wait(dev->reset_pin, 0, 200);
|
||||
io_utils_usleep(1000);
|
||||
}
|
||||
if (level == 1 || level == -1)
|
||||
{
|
||||
ZF_LOGD("Setting FPGA reset pin to 1");
|
||||
io_utils_write_gpio_with_wait(dev->reset_pin, 1, 200);
|
||||
io_utils_usleep(1000);
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,47 +0,0 @@
|
|||
#ifndef __CARIBOU_PROG_H__
|
||||
#define __CARIBOU_PROG_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <linux/types.h>
|
||||
#include "io_utils/io_utils.h"
|
||||
#include "io_utils/io_utils_spi.h"
|
||||
|
||||
/**
|
||||
* @brief caribou-sdr programmer context
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int cs_pin;
|
||||
int cdone_pin;
|
||||
int reset_pin;
|
||||
int verbose;
|
||||
|
||||
io_utils_spi_st* io_spi;
|
||||
|
||||
int io_spi_handle;
|
||||
int initialized;
|
||||
} caribou_prog_st;
|
||||
|
||||
int caribou_prog_init(caribou_prog_st *dev, io_utils_spi_st* io_spi);
|
||||
int caribou_prog_release(caribou_prog_st *dev);
|
||||
int caribou_prog_configure(caribou_prog_st *dev, char *bitfilename);
|
||||
int caribou_prog_configure_from_buffer( caribou_prog_st *dev,
|
||||
uint8_t *buffer,
|
||||
uint32_t buffer_size);
|
||||
|
||||
/*
|
||||
* Hard reset pin toggling function
|
||||
Level: if -1 => a full reset (1=>0=>1) cycle is performed
|
||||
if 0 / 1 => the pin is reset or set accordingly
|
||||
*/
|
||||
int caribou_prog_hard_reset(caribou_prog_st *dev, int level);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __CARIBOU_PROG_H__
|
|
@ -1,2 +0,0 @@
|
|||
# build directories
|
||||
build
|
|
@ -1,23 +0,0 @@
|
|||
cmake_minimum_required(VERSION 3.15)
|
||||
project(cariboulite)
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
|
||||
# Bring the headers
|
||||
#add_subdirectory(kernel "${CMAKE_CURRENT_BINARY_DIR}/kernel_build")
|
||||
|
||||
set(SUPER_DIR ${PROJECT_SOURCE_DIR}/..)
|
||||
include_directories(/.)
|
||||
include_directories(${SUPER_DIR})
|
||||
|
||||
# allows for wildcard additions:
|
||||
set(SOURCES_LIB caribou_smi.c smi_utils.c caribou_smi_modules.c)
|
||||
set(SOURCES ${SOURCES_LIB} test_caribou_smi.c)
|
||||
set(EXTERN_LIBS ${SUPER_DIR}/io_utils/build/libio_utils.a ${SUPER_DIR}/zf_log/build/libzf_log.a -lpthread)
|
||||
add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-missing-braces -Wno-unused-function -O3)
|
||||
|
||||
# Generate the static library from the sources
|
||||
add_library(caribou_smi STATIC ${SOURCES_LIB})
|
||||
#add_dependencies(caribou_smi smi_modules)
|
||||
|
||||
#add_executable(test_caribou_smi ${SOURCES})
|
||||
#target_link_libraries(test_caribou_smi ${EXTERN_LIBS} m rt pthread)
|
|
@ -1,77 +0,0 @@
|
|||
# Caribou-SMI API Driver
|
||||
This directory contains a user-mode interface code with the `bcm2835_smi` kernel module.
|
||||
To start working with the interface, some kernel modules need to be probed first.
|
||||
|
||||
By default, CaribouLite boards are pre-programmed to automatically probe the SMI modules on startup. Each CaribouLite contains an EEPROM device that in programmed during production with the updated device tree overlay (for more info [see here](../../../devicetrees/README.md)) file that loads on startup. This device tree "requests" loading the SMI associated kernel modules on startup. So if you have your CaribouLite board mounted on the 40-pin header, the module probing is not necessary.
|
||||
|
||||
To check whether these modules are probed, follow the next command in your Raspberry Pi terminal:
|
||||
```
|
||||
lsmod | grep smi
|
||||
```
|
||||
|
||||
you should be seeing the following output:
|
||||
```
|
||||
bcm2835_smi_dev 16384 0
|
||||
bcm2835_smi 20480 1 bcm2835_smi_dev
|
||||
```
|
||||
|
||||
If you do not see these modules, please check:
|
||||
1. The CaribouLite is properly mounted and powered.
|
||||
2. The CAribouLite is properly flashed - follow [this link](../../../../docs/flashing/README.md)
|
||||
|
||||
For detailed hardware-related information on the SMI interface, please click [here](../../../../docs/smi/README.md)
|
||||
|
||||
# The API
|
||||
## Initializing and closing the SMI instance
|
||||
|
||||
**Opening** the device and configuring the error-callback-function to be triggered once an error ocurred:
|
||||
```
|
||||
int caribou_smi_init(caribou_smi_st* dev,
|
||||
caribou_smi_error_callback error_cb,
|
||||
void* context);
|
||||
```
|
||||
|
||||
**Closing** the device:
|
||||
```
|
||||
int caribou_smi_close (caribou_smi_st* dev);
|
||||
```
|
||||
|
||||
## Reading and writing into the device
|
||||
|
||||
**Reading** into a buffer with timeout. If the SMI connected device (in this case, CaribouLite) doesn't have enough data to send, this function will wait till the timeout elapses. For non-blocking operation, please set `timeout = 0`
|
||||
```
|
||||
int caribou_smi_timeout_read(caribou_smi_st* dev,
|
||||
caribou_smi_address_en source,
|
||||
char* buffer,
|
||||
int size_of_buf,
|
||||
int timeout_num_millisec);
|
||||
```
|
||||
|
||||
```
|
||||
writing - TBD
|
||||
```
|
||||
|
||||
## Stream operations
|
||||
In most cases, working with file read/write synchronously is not the right choice. If we want an asynchronous operation with events and callbacks, we should setup a stream as follows:
|
||||
```
|
||||
int caribou_smi_setup_stream(caribou_smi_st* dev,
|
||||
caribou_smi_stream_type_en type,
|
||||
caribou_smi_channel_en channel,
|
||||
int batch_length, int num_buffers,
|
||||
caribou_smi_data_callback cb,
|
||||
void* serviced_context);
|
||||
```
|
||||
`batch_length` - is the length of a single buffer to serve (in bytes)
|
||||
`num_buffers` - is the number of batch buffers to allocate - for fluent buffer swapping
|
||||
`cb` - data callback function that is triggered every time a buffer is ready to be served.
|
||||
`serviced_context` - the data "requester" that is being serviced by the I/Q data. In most cases that is a higher layered driver / API.
|
||||
The returned integer is the stream ID used for further operations.
|
||||
|
||||
Notes: Once the stream is created it is operational **but paused!** to activate it use the following function (on the specific stream ID). This function is used also for pausing the stream (run = 0).
|
||||
```
|
||||
int caribou_smi_run_pause_stream (caribou_smi_st* dev, int id, int run);
|
||||
```
|
||||
Gracefully disposing the stream is done using the "destroy" function
|
||||
```
|
||||
int caribou_smi_destroy_stream(caribou_smi_st* dev, int id);
|
||||
```
|
|
@ -1,773 +0,0 @@
|
|||
#ifndef ZF_LOG_LEVEL
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#endif
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "CARIBOU_SMI"
|
||||
#include "zf_log/zf_log.h"
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/time.h>
|
||||
#include <sched.h>
|
||||
#include <pthread.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "caribou_smi.h"
|
||||
#include "smi_utils.h"
|
||||
#include "io_utils/io_utils.h"
|
||||
|
||||
//=========================================================================
|
||||
int caribou_smi_set_driver_streaming_state(caribou_smi_st* dev, smi_stream_state_en state)
|
||||
{
|
||||
int ret = ioctl(dev->filedesc, SMI_STREAM_IOC_SET_STREAM_STATUS, state);
|
||||
if (ret != 0)
|
||||
{
|
||||
ZF_LOGE("failed setting smi stream state (%d)", state);
|
||||
return -1;
|
||||
}
|
||||
dev->state = state;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
smi_stream_state_en caribou_smi_get_driver_streaming_state(caribou_smi_st* dev)
|
||||
{
|
||||
return dev->state;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
static void caribou_smi_print_smi_settings(caribou_smi_st* dev, struct smi_settings *settings)
|
||||
{
|
||||
printf("SMI SETTINGS:\n");
|
||||
printf(" width: %d\n", settings->data_width);
|
||||
printf(" pack: %c\n", settings->pack_data ? 'Y' : 'N');
|
||||
printf(" read setup: %d, strobe: %d, hold: %d, pace: %d\n", settings->read_setup_time, settings->read_strobe_time, settings->read_hold_time, settings->read_pace_time);
|
||||
printf(" write setup: %d, strobe: %d, hold: %d, pace: %d\n", settings->write_setup_time, settings->write_strobe_time, settings->write_hold_time, settings->write_pace_time);
|
||||
printf(" dma enable: %c, passthru enable: %c\n", settings->dma_enable ? 'Y':'N', settings->dma_passthrough_enable ? 'Y':'N');
|
||||
printf(" dma threshold read: %d, write: %d\n", settings->dma_read_thresh, settings->dma_write_thresh);
|
||||
printf(" dma panic threshold read: %d, write: %d\n", settings->dma_panic_read_thresh, settings->dma_panic_write_thresh);
|
||||
printf(" native kernel chunk size: %ld bytes\n", dev->native_batch_len);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
static int caribou_smi_get_smi_settings(caribou_smi_st *dev, struct smi_settings *settings, bool print)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = ioctl(dev->filedesc, BCM2835_SMI_IOC_GET_SETTINGS, settings);
|
||||
if (ret != 0)
|
||||
{
|
||||
ZF_LOGE("failed reading ioctl from smi fd (settings)");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = ioctl(dev->filedesc, SMI_STREAM_IOC_GET_NATIVE_BUF_SIZE, &dev->native_batch_len);
|
||||
if (ret != 0)
|
||||
{
|
||||
ZF_LOGE("failed reading native batch length, setting the default - this error is not fatal but we have wrong kernel drivers");
|
||||
dev->native_batch_len = (1024)*(1024)/2;
|
||||
}
|
||||
|
||||
//printf("DEBUG: native batch len: %lu\n", dev->native_batch_len);
|
||||
|
||||
if (print)
|
||||
{
|
||||
caribou_smi_print_smi_settings(dev, settings);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
static int caribou_smi_setup_settings (caribou_smi_st* dev, struct smi_settings *settings, bool print)
|
||||
{
|
||||
settings->read_setup_time = 0;
|
||||
settings->read_strobe_time = 5;
|
||||
settings->read_hold_time = 0;
|
||||
settings->read_pace_time = 0;
|
||||
|
||||
settings->write_setup_time = 0;
|
||||
settings->write_strobe_time = 5;
|
||||
settings->write_hold_time = 0;
|
||||
settings->write_pace_time = 0;
|
||||
|
||||
// 8 bit on each transmission (4 TRX per sample)
|
||||
settings->data_width = SMI_WIDTH_8BIT;
|
||||
|
||||
// Enable DMA
|
||||
settings->dma_enable = 1;
|
||||
|
||||
// Whether or not to pack multiple SMI transfers into a single 32 bit FIFO word
|
||||
settings->pack_data = 1;
|
||||
|
||||
// External DREQs enabled
|
||||
settings->dma_passthrough_enable = 1;
|
||||
|
||||
// RX DREQ Threshold Level.
|
||||
// A RX DREQ will be generated when the RX FIFO exceeds this threshold level.
|
||||
// This will instruct an external AXI RX DMA to read the RX FIFO.
|
||||
// If the DMA is set to perform burst reads, the threshold must ensure that there is
|
||||
// sufficient data in the FIFO to satisfy the burst
|
||||
// Instruction: Lower is faster response
|
||||
settings->dma_read_thresh = 1;
|
||||
|
||||
// TX DREQ Threshold Level.
|
||||
// A TX DREQ will be generated when the TX FIFO drops below this threshold level.
|
||||
// This will instruct an external AXI TX DMA to write more data to the TX FIFO.
|
||||
// Instruction: Higher is faster response
|
||||
settings->dma_write_thresh = 254;
|
||||
|
||||
// RX Panic Threshold level.
|
||||
// A RX Panic will be generated when the RX FIFO exceeds this threshold level.
|
||||
// This will instruct the AXI RX DMA to increase the priority of its bus requests.
|
||||
// Instruction: Lower is more aggressive
|
||||
settings->dma_panic_read_thresh = 16;
|
||||
|
||||
// TX Panic threshold level.
|
||||
// A TX Panic will be generated when the TX FIFO drops below this threshold level.
|
||||
// This will instruct the AXI TX DMA to increase the priority of its bus requests.
|
||||
// Instruction: Higher is more aggresive
|
||||
settings->dma_panic_write_thresh = 224;
|
||||
|
||||
if (print)
|
||||
{
|
||||
caribou_smi_print_smi_settings(dev, settings);
|
||||
}
|
||||
|
||||
if (ioctl(dev->filedesc, BCM2835_SMI_IOC_WRITE_SETTINGS, settings) != 0)
|
||||
{
|
||||
ZF_LOGE("failed writing ioctl to the smi fd (settings)");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// set the address line parameters
|
||||
int address_dir_offset = 2;
|
||||
if (ioctl(dev->filedesc, SMI_STREAM_IOC_SET_ADDR_DIR_OFFSET, address_dir_offset) != 0)
|
||||
{
|
||||
ZF_LOGE("failed writing ioctl to the smi fd (address_dir_offset)");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// set the address line parameters
|
||||
int address_channel_offset = 3;
|
||||
if (ioctl(dev->filedesc, SMI_STREAM_IOC_SET_ADDR_CH_OFFSET, address_channel_offset) != 0)
|
||||
{
|
||||
ZF_LOGE("failed writing ioctl to the smi fd (address_channel_offset)");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
static void caribou_smi_anayze_smi_debug(caribou_smi_st* dev, uint8_t *data, size_t len)
|
||||
{
|
||||
uint32_t error_counter_current = 0;
|
||||
int first_error = -1;
|
||||
uint32_t *values = (uint32_t*)data;
|
||||
|
||||
//smi_utils_dump_hex(buffer, 12);
|
||||
|
||||
if (dev->debug_mode == caribou_smi_lfsr)
|
||||
{
|
||||
for (size_t i = 0; i < len; i++)
|
||||
{
|
||||
if (data[i] != smi_utils_lfsr(dev->debug_data.last_correct_byte) || data[i] == 0)
|
||||
{
|
||||
if (first_error == -1) first_error = i;
|
||||
|
||||
dev->debug_data.error_accum_counter ++;
|
||||
error_counter_current ++;
|
||||
}
|
||||
dev->debug_data.last_correct_byte = data[i];
|
||||
}
|
||||
}
|
||||
|
||||
else if (dev->debug_mode == caribou_smi_push || dev->debug_mode == caribou_smi_pull)
|
||||
{
|
||||
for (size_t i = 0; i < len / 4; i++)
|
||||
{
|
||||
if (values[i] != CARIBOU_SMI_DEBUG_WORD)
|
||||
{
|
||||
if (first_error == -1) first_error = i * 4;
|
||||
|
||||
dev->debug_data.error_accum_counter += 4;
|
||||
error_counter_current += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dev->debug_data.cur_err_cnt = error_counter_current;
|
||||
dev->debug_data.bitrate = smi_calculate_performance(len, &dev->debug_data.last_time, dev->debug_data.bitrate);
|
||||
|
||||
dev->debug_data.error_rate = dev->debug_data.error_rate * 0.9 + (double)(error_counter_current) / (double)(len) * 0.1;
|
||||
if (dev->debug_data.error_rate < 1e-8)
|
||||
dev->debug_data.error_rate = 0.0;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
static void caribou_smi_print_debug_stats(caribou_smi_st* dev, uint8_t *buffer, size_t len)
|
||||
{
|
||||
static unsigned int count = 0;
|
||||
|
||||
count ++;
|
||||
if (count % 10 == 0)
|
||||
{
|
||||
printf("SMI DBG: ErrAccumCnt: %d, LastErrCnt: %d, ErrorRate: %.4g, bitrate: %.2f Mbps\n",
|
||||
dev->debug_data.error_accum_counter,
|
||||
dev->debug_data.cur_err_cnt,
|
||||
dev->debug_data.error_rate,
|
||||
dev->debug_data.bitrate);
|
||||
}
|
||||
//smi_utils_dump_hex(buffer, 16);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
static int caribou_smi_find_buffer_offset(caribou_smi_st* dev, uint8_t *buffer, size_t len)
|
||||
{
|
||||
size_t offs = 0;
|
||||
bool found = false;
|
||||
|
||||
if (len <= 4)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (dev->debug_mode == caribou_smi_none)
|
||||
{
|
||||
for (offs = 0; offs<(len-(CARIBOU_SMI_BYTES_PER_SAMPLE*4)); offs++)
|
||||
{
|
||||
uint32_t s1 = *((uint32_t*)(&buffer[offs]));
|
||||
uint32_t s2 = *((uint32_t*)(&buffer[offs+4]));
|
||||
uint32_t s3 = *((uint32_t*)(&buffer[offs+8]));
|
||||
uint32_t s4 = *((uint32_t*)(&buffer[offs+12]));
|
||||
|
||||
//printf("%d => %08X\n", offs, s);
|
||||
if ((s1 & 0xC001C000) == 0x80004000 &&
|
||||
(s2 & 0xC001C000) == 0x80004000 &&
|
||||
(s3 & 0xC001C000) == 0x80004000 &&
|
||||
(s4 & 0xC001C000) == 0x80004000)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (dev->debug_mode == caribou_smi_push || dev->debug_mode == caribou_smi_pull)
|
||||
{
|
||||
for (offs = 0; offs<(len-CARIBOU_SMI_BYTES_PER_SAMPLE); offs++)
|
||||
{
|
||||
uint32_t s = /*__builtin_bswap32*/(*((uint32_t*)(&buffer[offs])));
|
||||
//printf("%d => %08X, %08X\n", offs, s, caribou_smi_count_bit(s^CARIBOU_SMI_DEBUG_WORD));
|
||||
if (smi_utils_count_bit(s^CARIBOU_SMI_DEBUG_WORD) < 4)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// the lfsr option
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (found == false)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (int)offs;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
static int caribou_smi_rx_data_analyze(caribou_smi_st* dev,
|
||||
caribou_smi_channel_en channel,
|
||||
uint8_t* data, size_t data_length,
|
||||
caribou_smi_sample_complex_int16* samples_out,
|
||||
caribou_smi_sample_meta* meta_offset)
|
||||
{
|
||||
int offs = 0;
|
||||
size_t actual_length = data_length; // in bytes
|
||||
int size_shortening_samples = 0; // in samples
|
||||
uint32_t *actual_samples = (uint32_t*)(data);
|
||||
|
||||
caribou_smi_sample_complex_int16* cmplx_vec = samples_out;
|
||||
|
||||
// find the offset and adjust
|
||||
offs = caribou_smi_find_buffer_offset(dev, data, data_length);
|
||||
if (offs > 0)
|
||||
{
|
||||
//printf("OFFSET = %d\n", offs);
|
||||
}
|
||||
if (offs < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// adjust the lengths accroding to the sample mismatch
|
||||
// this may be accompanied by a few samples losses (sphoradic OS
|
||||
// scheduling) thus trying to stitch buffers one to another may
|
||||
// be not effective. The single sample is interpolated
|
||||
size_shortening_samples = (offs > 0) ? (offs / CARIBOU_SMI_BYTES_PER_SAMPLE + 1) : 0;
|
||||
actual_length -= size_shortening_samples * CARIBOU_SMI_BYTES_PER_SAMPLE;
|
||||
actual_samples = (uint32_t*)(data + offs);
|
||||
|
||||
// analyze the data
|
||||
if (dev->debug_mode != caribou_smi_none)
|
||||
{
|
||||
caribou_smi_anayze_smi_debug(dev, (uint8_t*)actual_samples, actual_length);
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned int i = 0;
|
||||
// Print buffer
|
||||
//smi_utils_dump_bin(buffer, 16);
|
||||
|
||||
// Data Structure:
|
||||
// [31:30] [ 29:17 ] [ 16 ] [ 15:14 ] [ 13:1 ] [ 0 ]
|
||||
// [ '10'] [ I sample ] [ '0' ] [ '01' ] [ Q sample ] [ 'S' ]
|
||||
|
||||
if (channel != caribou_smi_channel_2400)
|
||||
{ /* S1G */
|
||||
for (i = 0; i < actual_length / CARIBOU_SMI_BYTES_PER_SAMPLE; i++)
|
||||
{
|
||||
uint32_t s = /*__builtin_bswap32*/(actual_samples[i]);
|
||||
|
||||
if (meta_offset) meta_offset[i].sync = s & 0x00000001;
|
||||
if (cmplx_vec)
|
||||
{
|
||||
s >>= 1;
|
||||
cmplx_vec[i].q = s & 0x00001FFF; s >>= 13;
|
||||
s >>= 3;
|
||||
cmplx_vec[i].i = s & 0x00001FFF; s >>= 13;
|
||||
|
||||
if (cmplx_vec[i].i >= (int16_t)0x1000) cmplx_vec[i].i -= (int16_t)0x2000;
|
||||
if (cmplx_vec[i].q >= (int16_t)0x1000) cmplx_vec[i].q -= (int16_t)0x2000;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ /* HiF */
|
||||
for (i = 0; i < actual_length / CARIBOU_SMI_BYTES_PER_SAMPLE; i++)
|
||||
{
|
||||
uint32_t s = /*__builtin_bswap32*/(actual_samples[i]);
|
||||
|
||||
if (meta_offset) meta_offset[i].sync = s & 0x00000001;
|
||||
if (cmplx_vec)
|
||||
{
|
||||
s >>= 1;
|
||||
cmplx_vec[i].i = s & 0x00001FFF; s >>= 13;
|
||||
s >>= 3;
|
||||
cmplx_vec[i].q = s & 0x00001FFF; s >>= 13;
|
||||
|
||||
if (cmplx_vec[i].i >= (int16_t)0x1000) cmplx_vec[i].i -= (int16_t)0x2000;
|
||||
if (cmplx_vec[i].q >= (int16_t)0x1000) cmplx_vec[i].q -= (int16_t)0x2000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// last sample interpolation (linear for I and Q or preserve)
|
||||
if (size_shortening_samples > 0)
|
||||
{
|
||||
//cmplx_vec[i].i = 2*cmplx_vec[i-1].i - cmplx_vec[i-2].i;
|
||||
//cmplx_vec[i].q = 2*cmplx_vec[i-1].q - cmplx_vec[i-2].q;
|
||||
|
||||
cmplx_vec[i].i = 110*cmplx_vec[i-1].i/100 - cmplx_vec[i-2].i/10;
|
||||
cmplx_vec[i].q = 110*cmplx_vec[i-1].q/100 - cmplx_vec[i-2].q/10;
|
||||
}
|
||||
}
|
||||
|
||||
return offs;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
static int caribou_smi_poll(caribou_smi_st* dev, uint32_t timeout_num_millisec, smi_stream_direction_en dir)
|
||||
{
|
||||
int ret = 0;
|
||||
struct pollfd fds;
|
||||
fds.fd = dev->filedesc;
|
||||
|
||||
if (dir == smi_stream_dir_device_to_smi) fds.events = POLLIN;
|
||||
else if (dir == smi_stream_dir_smi_to_device) fds.events = POLLOUT;
|
||||
else return -1;
|
||||
|
||||
again:
|
||||
ret = poll(&fds, 1, timeout_num_millisec);
|
||||
if (ret == -1)
|
||||
{
|
||||
int error = errno;
|
||||
switch(error)
|
||||
{
|
||||
case EFAULT:
|
||||
ZF_LOGE("fds points outside the process's accessible address space");
|
||||
break;
|
||||
|
||||
case EINTR:
|
||||
case EAGAIN:
|
||||
ZF_LOGD("SMI filedesc select error - caught an interrupting signal");
|
||||
goto again;
|
||||
break;
|
||||
|
||||
case EINVAL:
|
||||
ZF_LOGE("The nfds value exceeds the RLIMIT_NOFILE value");
|
||||
break;
|
||||
|
||||
case ENOMEM:
|
||||
ZF_LOGE("Unable to allocate memory for kernel data structures.");
|
||||
break;
|
||||
|
||||
default: break;
|
||||
};
|
||||
return -1;
|
||||
}
|
||||
else if(ret == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return fds.revents & POLLIN || fds.revents & POLLOUT;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
static int caribou_smi_timeout_write(caribou_smi_st* dev,
|
||||
uint8_t* buffer,
|
||||
size_t len,
|
||||
uint32_t timeout_num_millisec)
|
||||
{
|
||||
int res = caribou_smi_poll(dev, timeout_num_millisec, smi_stream_dir_smi_to_device);
|
||||
|
||||
if (res < 0)
|
||||
{
|
||||
ZF_LOGD("poll error");
|
||||
return -1;
|
||||
}
|
||||
else if (res == 0) // timeout
|
||||
{
|
||||
//ZF_LOGD("===> smi write fd timeout");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return write(dev->filedesc, buffer, len);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
static int caribou_smi_timeout_read(caribou_smi_st* dev,
|
||||
uint8_t* buffer,
|
||||
size_t len,
|
||||
uint32_t timeout_num_millisec)
|
||||
{
|
||||
// try reading the file
|
||||
int ret = read(dev->filedesc, buffer, len);
|
||||
if (ret <= 0)
|
||||
{
|
||||
int res = caribou_smi_poll(dev, timeout_num_millisec, smi_stream_dir_device_to_smi);
|
||||
|
||||
if (res < 0)
|
||||
{
|
||||
ZF_LOGD("poll error");
|
||||
return -1;
|
||||
}
|
||||
else if (res == 0) // timeout
|
||||
{
|
||||
//ZF_LOGD("===> smi read fd timeout");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return read(dev->filedesc, buffer, len);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
void caribou_smi_setup_ios(caribou_smi_st* dev)
|
||||
{
|
||||
// setup the addresses
|
||||
io_utils_set_gpio_mode(2, io_utils_alt_1); // addr
|
||||
io_utils_set_gpio_mode(3, io_utils_alt_1); // addr
|
||||
|
||||
// Setup the bus I/Os
|
||||
// --------------------------------------------
|
||||
for (int i = 6; i <= 15; i++)
|
||||
{
|
||||
io_utils_set_gpio_mode(i, io_utils_alt_1); // 8xData + SWE + SOE
|
||||
}
|
||||
|
||||
io_utils_set_gpio_mode(24, io_utils_alt_1); // rwreq
|
||||
io_utils_set_gpio_mode(25, io_utils_alt_1); // rwreq
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
int caribou_smi_init(caribou_smi_st* dev,
|
||||
void* context)
|
||||
{
|
||||
char smi_file[] = "/dev/smi";
|
||||
struct smi_settings settings = {0};
|
||||
dev->read_temp_buffer = NULL;
|
||||
dev->write_temp_buffer = NULL;
|
||||
|
||||
ZF_LOGD("initializing caribou_smi");
|
||||
|
||||
// start from a defined state
|
||||
memset(dev, 0, sizeof(caribou_smi_st));
|
||||
|
||||
// checking the loaded modules
|
||||
// --------------------------------------------
|
||||
/*if (caribou_smi_check_modules(true) < 0)
|
||||
{
|
||||
ZF_LOGE("Problem reloading SMI kernel modules");
|
||||
return -1;
|
||||
}*/
|
||||
|
||||
// open the smi device file
|
||||
// --------------------------------------------
|
||||
int fd = open(smi_file, O_RDWR);
|
||||
if (fd < 0)
|
||||
{
|
||||
ZF_LOGE("couldn't open smi driver file '%s' (%s)", smi_file, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
dev->filedesc = fd;
|
||||
|
||||
// Setup the bus I/Os
|
||||
// --------------------------------------------
|
||||
caribou_smi_setup_ios(dev);
|
||||
|
||||
// Retrieve the current settings and modify
|
||||
// --------------------------------------------
|
||||
if (caribou_smi_get_smi_settings(dev, &settings, false) != 0)
|
||||
{
|
||||
caribou_smi_close (dev);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (caribou_smi_setup_settings(dev, &settings, false) != 0)
|
||||
{
|
||||
caribou_smi_close (dev);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Initialize temporary buffers
|
||||
// we add additional bytes to allow data synchronization corrections
|
||||
dev->read_temp_buffer = malloc (dev->native_batch_len + 1024);
|
||||
dev->write_temp_buffer = malloc (dev->native_batch_len + 1024);
|
||||
|
||||
if (dev->read_temp_buffer == NULL || dev->write_temp_buffer == NULL)
|
||||
{
|
||||
ZF_LOGE("smi temporary buffers allocation failed");
|
||||
caribou_smi_close (dev);
|
||||
return -1;
|
||||
}
|
||||
memset(&dev->debug_data, 0, sizeof(caribou_smi_debug_data_st));
|
||||
|
||||
dev->debug_mode = caribou_smi_none;
|
||||
dev->invert_iq = false;
|
||||
dev->sample_rate = CARIBOU_SMI_SAMPLE_RATE;
|
||||
dev->initialized = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
int caribou_smi_close (caribou_smi_st* dev)
|
||||
{
|
||||
// release temporary buffers
|
||||
if (dev->read_temp_buffer) free(dev->read_temp_buffer);
|
||||
if (dev->write_temp_buffer) free(dev->write_temp_buffer);
|
||||
|
||||
// close smi device file
|
||||
return close (dev->filedesc);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
void caribou_smi_set_sample_rate(caribou_smi_st* dev, uint32_t sample_rate)
|
||||
{
|
||||
if (sample_rate < 100000)
|
||||
{
|
||||
dev->sample_rate = 100000;
|
||||
}
|
||||
else if (sample_rate > CARIBOU_SMI_SAMPLE_RATE)
|
||||
{
|
||||
dev->sample_rate = CARIBOU_SMI_SAMPLE_RATE;
|
||||
}
|
||||
else
|
||||
{
|
||||
dev->sample_rate = sample_rate;
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
void caribou_smi_set_debug_mode(caribou_smi_st* dev, caribou_smi_debug_mode_en mode)
|
||||
{
|
||||
dev->debug_mode = mode;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
void caribou_smi_invert_iq(caribou_smi_st* dev, bool invert)
|
||||
{
|
||||
dev->invert_iq = invert;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
static int caribou_smi_calc_read_timeout(uint32_t sample_rate, size_t len)
|
||||
{
|
||||
uint32_t to_millisec = (2 * len * 1000) / sample_rate;
|
||||
if (to_millisec < 1) to_millisec = 1;
|
||||
return to_millisec;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
int caribou_smi_read(caribou_smi_st* dev, caribou_smi_channel_en channel,
|
||||
caribou_smi_sample_complex_int16* samples,
|
||||
caribou_smi_sample_meta* metadata,
|
||||
size_t length_samples)
|
||||
{
|
||||
caribou_smi_sample_complex_int16* sample_offset = samples;
|
||||
caribou_smi_sample_meta* meta_offset = metadata;
|
||||
size_t left_to_read = length_samples * CARIBOU_SMI_BYTES_PER_SAMPLE; // in bytes
|
||||
size_t read_so_far = 0; // in samples
|
||||
uint32_t to_millisec = caribou_smi_calc_read_timeout(dev->sample_rate, dev->native_batch_len);
|
||||
|
||||
while (left_to_read)
|
||||
{
|
||||
if (sample_offset) sample_offset = samples + read_so_far;
|
||||
if (meta_offset) meta_offset = metadata + read_so_far;
|
||||
|
||||
// current_read_len in bytes
|
||||
size_t current_read_len = ((left_to_read > dev->native_batch_len) ? dev->native_batch_len : left_to_read);
|
||||
|
||||
to_millisec = caribou_smi_calc_read_timeout(dev->sample_rate, current_read_len);
|
||||
int ret = caribou_smi_timeout_read(dev, dev->read_temp_buffer, current_read_len, to_millisec);
|
||||
if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if (ret == 0)
|
||||
{
|
||||
ZF_LOGD("Reading timed-out");
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
int data_affset = caribou_smi_rx_data_analyze(dev, channel, dev->read_temp_buffer, ret, sample_offset, meta_offset);
|
||||
if (data_affset < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// A special functionality for debug modes
|
||||
if (dev->debug_mode != caribou_smi_none)
|
||||
{
|
||||
caribou_smi_print_debug_stats(dev, dev->read_temp_buffer, ret);
|
||||
return -2;
|
||||
}
|
||||
}
|
||||
read_so_far += ret / CARIBOU_SMI_BYTES_PER_SAMPLE;
|
||||
left_to_read -= ret;
|
||||
}
|
||||
|
||||
return read_so_far;
|
||||
}
|
||||
|
||||
#define SMI_TX_SAMPLE_SOF (1<<2)
|
||||
#define SMI_TX_SAMPLE_MODEM_TX_CTRL (1<<1)
|
||||
#define SMI_TX_SAMPLE_COND_TX_CTRL (1<<0)
|
||||
//=========================================================================
|
||||
static void caribou_smi_generate_data(caribou_smi_st* dev, uint8_t* data, size_t data_length, caribou_smi_sample_complex_int16* sample_offset)
|
||||
{
|
||||
caribou_smi_sample_complex_int16* cmplx_vec = sample_offset;
|
||||
uint32_t *samples = (uint32_t*)(data);
|
||||
|
||||
for (unsigned int i = 0; i < (data_length / CARIBOU_SMI_BYTES_PER_SAMPLE); i++)
|
||||
{
|
||||
int32_t ii = cmplx_vec[i].i;
|
||||
int32_t qq = cmplx_vec[i].q;
|
||||
|
||||
uint32_t s = SMI_TX_SAMPLE_SOF | SMI_TX_SAMPLE_MODEM_TX_CTRL | SMI_TX_SAMPLE_COND_TX_CTRL; s <<= 5;
|
||||
s |= (ii >> 8) & 0x1F; s <<= 8;
|
||||
s |= (ii >> 1) & 0x7F; s <<= 2;
|
||||
s |= (ii & 0x1); s <<= 6;
|
||||
s |= (qq >> 7) & 0x3F; s <<= 8;
|
||||
s |= (qq & 0x7F);
|
||||
|
||||
//if (i < 2) printf("0x%08X\n", s);
|
||||
|
||||
samples[i] = __builtin_bswap32(s);
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
int caribou_smi_write(caribou_smi_st* dev, caribou_smi_channel_en channel,
|
||||
caribou_smi_sample_complex_int16* samples, size_t length_samples)
|
||||
{
|
||||
size_t left_to_write = length_samples * CARIBOU_SMI_BYTES_PER_SAMPLE; // in bytes
|
||||
size_t written_so_far = 0; // in samples
|
||||
uint32_t to_millisec = (2 * length_samples * 1000) / CARIBOU_SMI_SAMPLE_RATE;
|
||||
if (to_millisec < 2) to_millisec = 2;
|
||||
|
||||
smi_stream_state_en state = smi_stream_tx_channel;
|
||||
|
||||
// apply the state
|
||||
if (caribou_smi_set_driver_streaming_state(dev, state) != 0)
|
||||
{
|
||||
printf("caribou_smi_set_driver_streaming_state -> Failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (left_to_write)
|
||||
{
|
||||
// prepare the buffer
|
||||
caribou_smi_sample_complex_int16* sample_offset = samples + written_so_far;
|
||||
size_t current_write_len = (left_to_write > dev->native_batch_len) ? dev->native_batch_len : left_to_write;
|
||||
|
||||
// make sure the written bytes length is a whole sample multiplication
|
||||
// if the number of remaining bytes is smaller than sample size -> finish;
|
||||
current_write_len &= 0xFFFFFFFC;
|
||||
if (!current_write_len) break;
|
||||
|
||||
caribou_smi_generate_data(dev, dev->write_temp_buffer, current_write_len, sample_offset);
|
||||
|
||||
int ret = caribou_smi_timeout_write(dev, dev->write_temp_buffer, current_write_len, to_millisec);
|
||||
if (ret < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if (ret == 0) break;
|
||||
|
||||
written_so_far += current_write_len / CARIBOU_SMI_BYTES_PER_SAMPLE;
|
||||
left_to_write -= ret;
|
||||
}
|
||||
|
||||
return written_so_far;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
size_t caribou_smi_get_native_batch_samples(caribou_smi_st* dev)
|
||||
{
|
||||
//printf("DEBUG: native batch len: %lu\n", dev->native_batch_len / CARIBOU_SMI_BYTES_PER_SAMPLE);
|
||||
return (dev->native_batch_len / CARIBOU_SMI_BYTES_PER_SAMPLE);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
int caribou_smi_flush_fifo(caribou_smi_st* dev)
|
||||
{
|
||||
if (!dev) return -1;
|
||||
if (!dev->initialized) return -1;
|
||||
int ret = ioctl(dev->filedesc, SMI_STREAM_IOC_FLUSH_FIFO, 1);
|
||||
if (ret != 0)
|
||||
{
|
||||
ZF_LOGE("failed flushing driver fifos");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
#ifndef __CARIBOU_SMI_H__
|
||||
#define __CARIBOU_SMI_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
#include "kernel/bcm2835_smi.h"
|
||||
#include "kernel/smi_stream_dev.h"
|
||||
|
||||
// DEBUG Information
|
||||
typedef enum
|
||||
{
|
||||
caribou_smi_none = 0,
|
||||
caribou_smi_lfsr = 1,
|
||||
caribou_smi_push = 2,
|
||||
caribou_smi_pull = 3,
|
||||
} caribou_smi_debug_mode_en;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t error_accum_counter;
|
||||
uint32_t cur_err_cnt;
|
||||
uint8_t last_correct_byte;
|
||||
double error_rate;
|
||||
uint32_t cnt;
|
||||
double bitrate;
|
||||
struct timeval last_time;
|
||||
} caribou_smi_debug_data_st;
|
||||
|
||||
#define CARIBOU_SMI_DEBUG_WORD (0xABCDEF01)
|
||||
#define CARIBOU_SMI_BYTES_PER_SAMPLE (4)
|
||||
#define CARIBOU_SMI_SAMPLE_RATE (4000000)
|
||||
|
||||
typedef enum
|
||||
{
|
||||
caribou_smi_channel_900 = smi_stream_channel_0,
|
||||
caribou_smi_channel_2400 = smi_stream_channel_1,
|
||||
} caribou_smi_channel_en;
|
||||
|
||||
|
||||
// Data container
|
||||
#pragma pack(1)
|
||||
// associated with CS16 - total 4 bytes / element
|
||||
typedef struct
|
||||
{
|
||||
int16_t i; // LSB
|
||||
int16_t q; // MSB
|
||||
} caribou_smi_sample_complex_int16;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t sync;
|
||||
} caribou_smi_sample_meta;
|
||||
#pragma pack()
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int initialized;
|
||||
int filedesc;
|
||||
size_t native_batch_len;
|
||||
uint32_t sample_rate;
|
||||
smi_stream_state_en state;
|
||||
|
||||
uint8_t *read_temp_buffer;
|
||||
uint8_t *write_temp_buffer;
|
||||
|
||||
bool invert_iq;
|
||||
|
||||
// debugging
|
||||
caribou_smi_debug_mode_en debug_mode;
|
||||
caribou_smi_debug_data_st debug_data;
|
||||
} caribou_smi_st;
|
||||
|
||||
int caribou_smi_init(caribou_smi_st* dev,
|
||||
void* context);
|
||||
int caribou_smi_close (caribou_smi_st* dev);
|
||||
int caribou_smi_check_modules(bool reload);
|
||||
|
||||
void caribou_smi_invert_iq(caribou_smi_st* dev, bool invert);
|
||||
|
||||
void caribou_smi_set_debug_mode(caribou_smi_st* dev, caribou_smi_debug_mode_en mode);
|
||||
int caribou_smi_set_driver_streaming_state(caribou_smi_st* dev, smi_stream_state_en state);
|
||||
smi_stream_state_en caribou_smi_get_driver_streaming_state(caribou_smi_st* dev);
|
||||
|
||||
int caribou_smi_read(caribou_smi_st* dev, caribou_smi_channel_en channel,
|
||||
caribou_smi_sample_complex_int16* buffer, caribou_smi_sample_meta* metadata, size_t length_samples);
|
||||
|
||||
int caribou_smi_write(caribou_smi_st* dev, caribou_smi_channel_en channel,
|
||||
caribou_smi_sample_complex_int16* buffer, size_t length_samples);
|
||||
|
||||
size_t caribou_smi_get_native_batch_samples(caribou_smi_st* dev);
|
||||
|
||||
void caribou_smi_setup_ios(caribou_smi_st* dev);
|
||||
void caribou_smi_set_sample_rate(caribou_smi_st* dev, uint32_t sample_rate);
|
||||
int caribou_smi_flush_fifo(caribou_smi_st* dev);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __CARIBOU_SMI_H__
|
|
@ -1,118 +0,0 @@
|
|||
#ifndef ZF_LOG_LEVEL
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#endif
|
||||
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "CARIBOU_SMI_MODULES"
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "zf_log/zf_log.h"
|
||||
#include "caribou_smi.h"
|
||||
#include "kernel/smi_stream_dev_gen.h"
|
||||
|
||||
|
||||
#define delete_module(name, flags) syscall(__NR_delete_module, name, flags)
|
||||
#define init_module(module_image, len, param_values) syscall(__NR_init_module, module_image, len, param_values)
|
||||
#define finit_module(fd, param_values, flags) syscall(__NR_finit_module, fd, param_values, flags)
|
||||
|
||||
//===========================================================
|
||||
int caribou_smi_check_modules_loaded(char* mod_name)
|
||||
{
|
||||
char line[256] = {0};
|
||||
int found = 0;
|
||||
FILE *fid = fopen ("/proc/modules", "r");
|
||||
if (fid == NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (fgets(line, sizeof(line), fid))
|
||||
{
|
||||
char* srch = strstr(line, mod_name);
|
||||
if (srch != NULL)
|
||||
{
|
||||
if (!isspace(srch[strlen(mod_name)])) continue;
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
fclose(fid);
|
||||
return found;
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
int caribou_smi_remove_module(char* module_name)
|
||||
{
|
||||
if (delete_module(module_name, O_NONBLOCK) != 0)
|
||||
{
|
||||
ZF_LOGE("Module removing '%s' failed", module_name);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
int caribou_smi_insert_smi_modules(char* module_name,
|
||||
uint8_t* buffer,
|
||||
size_t len,
|
||||
const char* params)
|
||||
{
|
||||
if (init_module(buffer, len, params) != 0)
|
||||
{
|
||||
ZF_LOGE("Module insertion '%s' failed", module_name);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
int caribou_smi_check_modules(bool reload)
|
||||
{
|
||||
int ret = 0;
|
||||
int bcm_smi_dev_loaded = caribou_smi_check_modules_loaded("bcm2835_smi_dev");
|
||||
int smi_stream_dev_loaded = caribou_smi_check_modules_loaded("smi_stream_dev");
|
||||
int bcm_smi_loaded = caribou_smi_check_modules_loaded("bcm2835_smi");
|
||||
|
||||
if (bcm_smi_loaded != 1)
|
||||
{
|
||||
ZF_LOGE("SMI base driver not loaded - check device tree");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (smi_stream_dev_loaded == 1 && reload)
|
||||
{
|
||||
ZF_LOGD("Unloading smi-stream module");
|
||||
ret = caribou_smi_remove_module("smi_stream_dev");
|
||||
smi_stream_dev_loaded = 0;
|
||||
}
|
||||
|
||||
if (bcm_smi_dev_loaded == 1)
|
||||
{
|
||||
ret = caribou_smi_remove_module("bcm2835_smi_dev");
|
||||
}
|
||||
|
||||
|
||||
if (ret != 0)
|
||||
{
|
||||
ZF_LOGE("Error unloading module from system");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!smi_stream_dev_loaded || reload)
|
||||
{
|
||||
ZF_LOGD("Loading smi-stream module");
|
||||
return caribou_smi_insert_smi_modules("smi_stream_dev", smi_stream_dev, sizeof(smi_stream_dev), "");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
# build directories
|
||||
build
|
|
@ -1,3 +0,0 @@
|
|||
This directory contains generated files.
|
||||
Do not edit directly.
|
||||
Editing can be done through the "[root]/driver/" directory.
|
|
@ -1,391 +0,0 @@
|
|||
/**
|
||||
* Declarations and definitions for Broadcom's Secondary Memory Interface
|
||||
*
|
||||
* Written by Luke Wren <luke@raspberrypi.org>
|
||||
* Copyright (c) 2015, Raspberry Pi (Trading) Ltd.
|
||||
* Copyright (c) 2010-2012 Broadcom. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The names of the above-listed copyright holders may not be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* ALTERNATIVELY, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") version 2, as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef BCM2835_SMI_H
|
||||
#define BCM2835_SMI_H
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
|
||||
#ifndef __KERNEL__
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
|
||||
#define BCM2835_SMI_IOC_MAGIC 0x1
|
||||
#define BCM2835_SMI_INVALID_HANDLE (~0)
|
||||
|
||||
/* IOCTLs 0x100...0x1ff are not device-specific - we can use them */
|
||||
#define BCM2835_SMI_IOC_GET_SETTINGS _IO(BCM2835_SMI_IOC_MAGIC, 0)
|
||||
#define BCM2835_SMI_IOC_WRITE_SETTINGS _IO(BCM2835_SMI_IOC_MAGIC, 1)
|
||||
#define BCM2835_SMI_IOC_ADDRESS _IO(BCM2835_SMI_IOC_MAGIC, 2)
|
||||
#define BCM2835_SMI_IOC_MAX 2
|
||||
|
||||
#define SMI_WIDTH_8BIT 0
|
||||
#define SMI_WIDTH_16BIT 1
|
||||
#define SMI_WIDTH_9BIT 2
|
||||
#define SMI_WIDTH_18BIT 3
|
||||
|
||||
/* max number of bytes where DMA will not be used */
|
||||
#define DMA_THRESHOLD_BYTES 128
|
||||
#define DMA_BOUNCE_BUFFER_SIZE (1024 * 1024 / 2)
|
||||
#define DMA_BOUNCE_BUFFER_COUNT 3
|
||||
|
||||
|
||||
struct smi_settings {
|
||||
int data_width;
|
||||
/* Whether or not to pack multiple SMI transfers into a
|
||||
single 32 bit FIFO word */
|
||||
bool pack_data;
|
||||
|
||||
/* Timing for reads (writes the same but for WE)
|
||||
*
|
||||
* OE ----------+ +--------------------
|
||||
* | |
|
||||
* +----------+
|
||||
* SD -<==============================>-----------
|
||||
* SA -<=========================================>-
|
||||
* <-setup-> <-strobe -> <-hold -> <- pace ->
|
||||
*/
|
||||
|
||||
int read_setup_time;
|
||||
int read_hold_time;
|
||||
int read_pace_time;
|
||||
int read_strobe_time;
|
||||
|
||||
int write_setup_time;
|
||||
int write_hold_time;
|
||||
int write_pace_time;
|
||||
int write_strobe_time;
|
||||
|
||||
bool dma_enable; /* DREQs */
|
||||
bool dma_passthrough_enable; /* External DREQs */
|
||||
int dma_read_thresh;
|
||||
int dma_write_thresh;
|
||||
int dma_panic_read_thresh;
|
||||
int dma_panic_write_thresh;
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
*
|
||||
* Declare exported SMI functions
|
||||
*
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#include <linux/dmaengine.h> /* for enum dma_transfer_direction */
|
||||
#include <linux/of.h>
|
||||
#include <linux/semaphore.h>
|
||||
|
||||
struct bcm2835_smi_instance;
|
||||
|
||||
struct bcm2835_smi_bounce_info {
|
||||
struct semaphore callback_sem;
|
||||
void *buffer[DMA_BOUNCE_BUFFER_COUNT];
|
||||
dma_addr_t phys[DMA_BOUNCE_BUFFER_COUNT];
|
||||
struct scatterlist sgl[DMA_BOUNCE_BUFFER_COUNT];
|
||||
};
|
||||
|
||||
|
||||
void bcm2835_smi_set_regs_from_settings(struct bcm2835_smi_instance *);
|
||||
|
||||
struct smi_settings *bcm2835_smi_get_settings_from_regs(
|
||||
struct bcm2835_smi_instance *inst);
|
||||
|
||||
void bcm2835_smi_write_buf(
|
||||
struct bcm2835_smi_instance *inst,
|
||||
const void *buf,
|
||||
size_t n_bytes);
|
||||
|
||||
void bcm2835_smi_read_buf(
|
||||
struct bcm2835_smi_instance *inst,
|
||||
void *buf,
|
||||
size_t n_bytes);
|
||||
|
||||
void bcm2835_smi_set_address(struct bcm2835_smi_instance *inst,
|
||||
unsigned int address);
|
||||
|
||||
ssize_t bcm2835_smi_user_dma(
|
||||
struct bcm2835_smi_instance *inst,
|
||||
enum dma_transfer_direction dma_dir,
|
||||
char __user *user_ptr,
|
||||
size_t count,
|
||||
struct bcm2835_smi_bounce_info **bounce);
|
||||
|
||||
struct bcm2835_smi_instance *bcm2835_smi_get(struct device_node *node);
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
/****************************************************************
|
||||
*
|
||||
* Implementation-only declarations
|
||||
*
|
||||
****************************************************************/
|
||||
|
||||
#ifdef BCM2835_SMI_IMPLEMENTATION
|
||||
|
||||
/* Clock manager registers for SMI clock: */
|
||||
#define CM_SMI_BASE_ADDRESS ((BCM2708_PERI_BASE) + 0x1010b0)
|
||||
/* Clock manager "password" to protect registers from spurious writes */
|
||||
#define CM_PWD (0x5a << 24)
|
||||
|
||||
#define CM_SMI_CTL 0x00
|
||||
#define CM_SMI_DIV 0x04
|
||||
|
||||
#define CM_SMI_CTL_FLIP (1 << 8)
|
||||
#define CM_SMI_CTL_BUSY (1 << 7)
|
||||
#define CM_SMI_CTL_KILL (1 << 5)
|
||||
#define CM_SMI_CTL_ENAB (1 << 4)
|
||||
#define CM_SMI_CTL_SRC_MASK (0xf)
|
||||
#define CM_SMI_CTL_SRC_OFFS (0)
|
||||
|
||||
#define CM_SMI_DIV_DIVI_MASK (0xf << 12)
|
||||
#define CM_SMI_DIV_DIVI_OFFS (12)
|
||||
#define CM_SMI_DIV_DIVF_MASK (0xff << 4)
|
||||
#define CM_SMI_DIV_DIVF_OFFS (4)
|
||||
|
||||
/* SMI register mapping:*/
|
||||
#define SMI_BASE_ADDRESS ((BCM2708_PERI_BASE) + 0x600000)
|
||||
|
||||
#define SMICS 0x00 /* control + status register */
|
||||
#define SMIL 0x04 /* length/count (n external txfers) */
|
||||
#define SMIA 0x08 /* address register */
|
||||
#define SMID 0x0c /* data register */
|
||||
#define SMIDSR0 0x10 /* device 0 read settings */
|
||||
#define SMIDSW0 0x14 /* device 0 write settings */
|
||||
#define SMIDSR1 0x18 /* device 1 read settings */
|
||||
#define SMIDSW1 0x1c /* device 1 write settings */
|
||||
#define SMIDSR2 0x20 /* device 2 read settings */
|
||||
#define SMIDSW2 0x24 /* device 2 write settings */
|
||||
#define SMIDSR3 0x28 /* device 3 read settings */
|
||||
#define SMIDSW3 0x2c /* device 3 write settings */
|
||||
#define SMIDC 0x30 /* DMA control registers */
|
||||
#define SMIDCS 0x34 /* direct control/status register */
|
||||
#define SMIDA 0x38 /* direct address register */
|
||||
#define SMIDD 0x3c /* direct data registers */
|
||||
#define SMIFD 0x40 /* FIFO debug register */
|
||||
|
||||
|
||||
|
||||
/* Control and Status register bits:
|
||||
* SMICS_RXF : RX fifo full: 1 when RX fifo is full
|
||||
* SMICS_TXE : TX fifo empty: 1 when empty.
|
||||
* SMICS_RXD : RX fifo contains data: 1 when there is data.
|
||||
* SMICS_TXD : TX fifo can accept data: 1 when true.
|
||||
* SMICS_RXR : RX fifo needs reading: 1 when fifo more than 3/4 full, or
|
||||
* when "DONE" and fifo not emptied.
|
||||
* SMICS_TXW : TX fifo needs writing: 1 when less than 1/4 full.
|
||||
* SMICS_AFERR : AXI FIFO error: 1 when fifo read when empty or written
|
||||
* when full. Write 1 to clear.
|
||||
* SMICS_EDREQ : 1 when external DREQ received.
|
||||
* SMICS_PXLDAT : Pixel data: write 1 to enable pixel transfer modes.
|
||||
* SMICS_SETERR : 1 if there was an error writing to setup regs (e.g.
|
||||
* tx was in progress). Write 1 to clear.
|
||||
* SMICS_PVMODE : Set to 1 to enable pixel valve mode.
|
||||
* SMICS_INTR : Set to 1 to enable interrupt on RX.
|
||||
* SMICS_INTT : Set to 1 to enable interrupt on TX.
|
||||
* SMICS_INTD : Set to 1 to enable interrupt on DONE condition.
|
||||
* SMICS_TEEN : Tear effect mode enabled: Programmed transfers will wait
|
||||
* for a TE trigger before writing.
|
||||
* SMICS_PAD1 : Padding settings for external transfers. For writes: the
|
||||
* number of bytes initially written to the TX fifo that
|
||||
* SMICS_PAD0 : should be ignored. For reads: the number of bytes that will
|
||||
* be read before the data, and should be dropped.
|
||||
* SMICS_WRITE : Transfer direction: 1 = write to external device, 0 = read
|
||||
* SMICS_CLEAR : Write 1 to clear the FIFOs.
|
||||
* SMICS_START : Write 1 to start the programmed transfer.
|
||||
* SMICS_ACTIVE : Reads as 1 when a programmed transfer is underway.
|
||||
* SMICS_DONE : Reads as 1 when transfer finished. For RX, not set until
|
||||
* FIFO emptied.
|
||||
* SMICS_ENABLE : Set to 1 to enable the SMI peripheral, 0 to disable.
|
||||
*/
|
||||
|
||||
#define SMICS_RXF (1 << 31)
|
||||
#define SMICS_TXE (1 << 30)
|
||||
#define SMICS_RXD (1 << 29)
|
||||
#define SMICS_TXD (1 << 28)
|
||||
#define SMICS_RXR (1 << 27)
|
||||
#define SMICS_TXW (1 << 26)
|
||||
#define SMICS_AFERR (1 << 25)
|
||||
#define SMICS_EDREQ (1 << 15)
|
||||
#define SMICS_PXLDAT (1 << 14)
|
||||
#define SMICS_SETERR (1 << 13)
|
||||
#define SMICS_PVMODE (1 << 12)
|
||||
#define SMICS_INTR (1 << 11)
|
||||
#define SMICS_INTT (1 << 10)
|
||||
#define SMICS_INTD (1 << 9)
|
||||
#define SMICS_TEEN (1 << 8)
|
||||
#define SMICS_PAD1 (1 << 7)
|
||||
#define SMICS_PAD0 (1 << 6)
|
||||
#define SMICS_WRITE (1 << 5)
|
||||
#define SMICS_CLEAR (1 << 4)
|
||||
#define SMICS_START (1 << 3)
|
||||
#define SMICS_ACTIVE (1 << 2)
|
||||
#define SMICS_DONE (1 << 1)
|
||||
#define SMICS_ENABLE (1 << 0)
|
||||
|
||||
/* Address register bits: */
|
||||
|
||||
#define SMIA_DEVICE_MASK ((1 << 9) | (1 << 8))
|
||||
#define SMIA_DEVICE_OFFS (8)
|
||||
#define SMIA_ADDR_MASK (0x3f) /* bits 5 -> 0 */
|
||||
#define SMIA_ADDR_OFFS (0)
|
||||
|
||||
/* DMA control register bits:
|
||||
* SMIDC_DMAEN : DMA enable: set 1: DMA requests will be issued.
|
||||
* SMIDC_DMAP : DMA passthrough: when set to 0, top two data pins are used by
|
||||
* SMI as usual. When set to 1, the top two pins are used for
|
||||
* external DREQs: pin 16 read request, 17 write.
|
||||
* SMIDC_PANIC* : Threshold at which DMA will panic during read/write.
|
||||
* SMIDC_REQ* : Threshold at which DMA will generate a DREQ.
|
||||
*/
|
||||
|
||||
#define SMIDC_DMAEN (1 << 28)
|
||||
#define SMIDC_DMAP (1 << 24)
|
||||
#define SMIDC_PANICR_MASK (0x3f << 18)
|
||||
#define SMIDC_PANICR_OFFS (18)
|
||||
#define SMIDC_PANICW_MASK (0x3f << 12)
|
||||
#define SMIDC_PANICW_OFFS (12)
|
||||
#define SMIDC_REQR_MASK (0x3f << 6)
|
||||
#define SMIDC_REQR_OFFS (6)
|
||||
#define SMIDC_REQW_MASK (0x3f)
|
||||
#define SMIDC_REQW_OFFS (0)
|
||||
|
||||
/* Device settings register bits: same for all 4 (or 3?) device register sets.
|
||||
* Device read settings:
|
||||
* SMIDSR_RWIDTH : Read transfer width. 00 = 8bit, 01 = 16bit,
|
||||
* 10 = 18bit, 11 = 9bit.
|
||||
* SMIDSR_RSETUP : Read setup time: number of core cycles between chip
|
||||
* select/address and read strobe. Min 1, max 64.
|
||||
* SMIDSR_MODE68 : 1 for System 68 mode (i.e. enable + direction pins,
|
||||
* rather than OE + WE pin)
|
||||
* SMIDSR_FSETUP : If set to 1, setup time only applies to first
|
||||
* transfer after address change.
|
||||
* SMIDSR_RHOLD : Number of core cycles between read strobe going
|
||||
* inactive and CS/address going inactive. Min 1, max 64
|
||||
* SMIDSR_RPACEALL : When set to 1, this device's RPACE value will always
|
||||
* be used for the next transaction, even if it is not
|
||||
* to this device.
|
||||
* SMIDSR_RPACE : Number of core cycles spent waiting between CS
|
||||
* deassert and start of next transfer. Min 1, max 128
|
||||
* SMIDSR_RDREQ : 1 = use external DMA request on SD16 to pace reads
|
||||
* from device. Must also set DMAP in SMICS.
|
||||
* SMIDSR_RSTROBE : Number of cycles to assert the read strobe.
|
||||
* min 1, max 128.
|
||||
*/
|
||||
#define SMIDSR_RWIDTH_MASK ((1<<31)|(1<<30))
|
||||
#define SMIDSR_RWIDTH_OFFS (30)
|
||||
#define SMIDSR_RSETUP_MASK (0x3f << 24)
|
||||
#define SMIDSR_RSETUP_OFFS (24)
|
||||
#define SMIDSR_MODE68 (1 << 23)
|
||||
#define SMIDSR_FSETUP (1 << 22)
|
||||
#define SMIDSR_RHOLD_MASK (0x3f << 16)
|
||||
#define SMIDSR_RHOLD_OFFS (16)
|
||||
#define SMIDSR_RPACEALL (1 << 15)
|
||||
#define SMIDSR_RPACE_MASK (0x7f << 8)
|
||||
#define SMIDSR_RPACE_OFFS (8)
|
||||
#define SMIDSR_RDREQ (1 << 7)
|
||||
#define SMIDSR_RSTROBE_MASK (0x7f)
|
||||
#define SMIDSR_RSTROBE_OFFS (0)
|
||||
|
||||
/* Device write settings:
|
||||
* SMIDSW_WWIDTH : Write transfer width. 00 = 8bit, 01 = 16bit,
|
||||
* 10= 18bit, 11 = 9bit.
|
||||
* SMIDSW_WSETUP : Number of cycles between CS assert and write strobe.
|
||||
* Min 1, max 64.
|
||||
* SMIDSW_WFORMAT : Pixel format of input. 0 = 16bit RGB 565,
|
||||
* 1 = 32bit RGBA 8888
|
||||
* SMIDSW_WSWAP : 1 = swap pixel data bits. (Use with SMICS_PXLDAT)
|
||||
* SMIDSW_WHOLD : Time between WE deassert and CS deassert. 1 to 64
|
||||
* SMIDSW_WPACEALL : 1: this device's WPACE will be used for the next
|
||||
* transfer, regardless of that transfer's device.
|
||||
* SMIDSW_WPACE : Cycles between CS deassert and next CS assert.
|
||||
* Min 1, max 128
|
||||
* SMIDSW_WDREQ : Use external DREQ on pin 17 to pace writes. DMAP must
|
||||
* be set in SMICS.
|
||||
* SMIDSW_WSTROBE : Number of cycles to assert the write strobe.
|
||||
* Min 1, max 128
|
||||
*/
|
||||
#define SMIDSW_WWIDTH_MASK ((1<<31)|(1<<30))
|
||||
#define SMIDSW_WWIDTH_OFFS (30)
|
||||
#define SMIDSW_WSETUP_MASK (0x3f << 24)
|
||||
#define SMIDSW_WSETUP_OFFS (24)
|
||||
#define SMIDSW_WFORMAT (1 << 23)
|
||||
#define SMIDSW_WSWAP (1 << 22)
|
||||
#define SMIDSW_WHOLD_MASK (0x3f << 16)
|
||||
#define SMIDSW_WHOLD_OFFS (16)
|
||||
#define SMIDSW_WPACEALL (1 << 15)
|
||||
#define SMIDSW_WPACE_MASK (0x7f << 8)
|
||||
#define SMIDSW_WPACE_OFFS (8)
|
||||
#define SMIDSW_WDREQ (1 << 7)
|
||||
#define SMIDSW_WSTROBE_MASK (0x7f)
|
||||
#define SMIDSW_WSTROBE_OFFS (0)
|
||||
|
||||
/* Direct transfer control + status register
|
||||
* SMIDCS_WRITE : Direction of transfer: 1 -> write, 0 -> read
|
||||
* SMIDCS_DONE : 1 when a transfer has finished. Write 1 to clear.
|
||||
* SMIDCS_START : Write 1 to start a transfer, if one is not already underway.
|
||||
* SMIDCE_ENABLE: Write 1 to enable SMI in direct mode.
|
||||
*/
|
||||
|
||||
#define SMIDCS_WRITE (1 << 3)
|
||||
#define SMIDCS_DONE (1 << 2)
|
||||
#define SMIDCS_START (1 << 1)
|
||||
#define SMIDCS_ENABLE (1 << 0)
|
||||
|
||||
/* Direct transfer address register
|
||||
* SMIDA_DEVICE : Indicates which of the device settings banks should be used.
|
||||
* SMIDA_ADDR : The value to be asserted on the address pins.
|
||||
*/
|
||||
|
||||
#define SMIDA_DEVICE_MASK ((1<<9)|(1<<8))
|
||||
#define SMIDA_DEVICE_OFFS (8)
|
||||
#define SMIDA_ADDR_MASK (0x3f)
|
||||
#define SMIDA_ADDR_OFFS (0)
|
||||
|
||||
/* FIFO debug register
|
||||
* SMIFD_FLVL : The high-tide mark of FIFO count during the most recent txfer
|
||||
* SMIFD_FCNT : The current FIFO count.
|
||||
*/
|
||||
#define SMIFD_FLVL_MASK (0x3f << 8)
|
||||
#define SMIFD_FLVL_OFFS (8)
|
||||
#define SMIFD_FCNT_MASK (0x3f)
|
||||
#define SMIFD_FCNT_OFFS (0)
|
||||
|
||||
#endif /* BCM2835_SMI_IMPLEMENTATION */
|
||||
|
||||
#endif /* BCM2835_SMI_H */
|
|
@ -1,111 +0,0 @@
|
|||
/**
|
||||
* Declarations and definitions for Broadcom's Secondary Memory Interface
|
||||
*
|
||||
* Written by David Michaeli <cariboulabs.co@gmail.com>
|
||||
* Copyright (c) 2021, CaribouLabs.co
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions, and the following disclaimer,
|
||||
* without modification.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The names of the above-listed copyright holders may not be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* ALTERNATIVELY, this software may be distributed under the terms of the
|
||||
* GNU General Public License ("GPL") version 2, as published by the Free
|
||||
* Software Foundation.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _SMI_STREAM_DEV_H_
|
||||
#define _SMI_STREAM_DEV_H_
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
|
||||
#ifndef __KERNEL__
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#else
|
||||
#define BCM2835_SMI_IMPLEMENTATION
|
||||
#include <linux/broadcom/bcm2835_smi.h>
|
||||
#endif
|
||||
|
||||
#define DEVICE_NAME "smi-stream-dev"
|
||||
#define DRIVER_NAME "smi-stream-dev"
|
||||
#define DEVICE_MINOR 0
|
||||
|
||||
typedef enum
|
||||
{
|
||||
smi_stream_dir_smi_to_device = 0, // device data-bus is highZ (TX)
|
||||
smi_stream_dir_device_to_smi = 1, // device data-bus is push-pull (RX)
|
||||
} smi_stream_direction_en;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
smi_stream_channel_0 = 0,
|
||||
smi_stream_channel_1 = 1,
|
||||
smi_stream_channel_max,
|
||||
} smi_stream_channel_en;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
smi_stream_idle = 0,
|
||||
smi_stream_rx_channel_0 = 1,
|
||||
smi_stream_rx_channel_1 = 2,
|
||||
smi_stream_tx_channel = 3,
|
||||
} smi_stream_state_en;
|
||||
|
||||
#ifdef __KERNEL__
|
||||
struct bcm2835_smi_instance {
|
||||
struct device *dev;
|
||||
struct smi_settings settings;
|
||||
|
||||
__iomem void *smi_regs_ptr;
|
||||
dma_addr_t smi_regs_busaddr;
|
||||
|
||||
struct dma_chan *dma_chan;
|
||||
struct dma_slave_config dma_config;
|
||||
|
||||
struct bcm2835_smi_bounce_info bounce;
|
||||
|
||||
struct scatterlist buffer_sgl;
|
||||
|
||||
struct clk *clk;
|
||||
|
||||
/* Sometimes we are called into in an atomic context (e.g. by
|
||||
JFFS2 + MTD) so we can't use a mutex */
|
||||
spinlock_t transaction_lock;
|
||||
};
|
||||
#endif // __KERNEL__
|
||||
|
||||
// Expansion of ioctls
|
||||
#define SMI_STREAM_IOC_GET_NATIVE_BUF_SIZE _IO(BCM2835_SMI_IOC_MAGIC,(BCM2835_SMI_IOC_MAX+1))
|
||||
#define SMI_STREAM_IOC_SET_STREAM_STATUS _IO(BCM2835_SMI_IOC_MAGIC,(BCM2835_SMI_IOC_MAX+2))
|
||||
#define SMI_STREAM_IOC_SET_STREAM_IN_CHANNEL _IO(BCM2835_SMI_IOC_MAGIC,(BCM2835_SMI_IOC_MAX+3))
|
||||
#define SMI_STREAM_IOC_SET_FIFO_MULT _IO(BCM2835_SMI_IOC_MAGIC,(BCM2835_SMI_IOC_MAX+4))
|
||||
#define SMI_STREAM_IOC_SET_ADDR_DIR_OFFSET _IO(BCM2835_SMI_IOC_MAGIC,(BCM2835_SMI_IOC_MAX+5))
|
||||
#define SMI_STREAM_IOC_SET_ADDR_CH_OFFSET _IO(BCM2835_SMI_IOC_MAGIC,(BCM2835_SMI_IOC_MAX+6))
|
||||
#define SMI_STREAM_IOC_GET_FIFO_MULT _IO(BCM2835_SMI_IOC_MAGIC,(BCM2835_SMI_IOC_MAX+7))
|
||||
#define SMI_STREAM_IOC_GET_ADDR_DIR_OFFSET _IO(BCM2835_SMI_IOC_MAGIC,(BCM2835_SMI_IOC_MAX+8))
|
||||
#define SMI_STREAM_IOC_GET_ADDR_CH_OFFSET _IO(BCM2835_SMI_IOC_MAGIC,(BCM2835_SMI_IOC_MAX+9))
|
||||
#define SMI_STREAM_IOC_FLUSH_FIFO _IO(BCM2835_SMI_IOC_MAGIC,(BCM2835_SMI_IOC_MAX+10))
|
||||
|
||||
|
||||
#endif /* _SMI_STREAM_DEV_H_ */
|
|
@ -1,256 +0,0 @@
|
|||
#ifndef ZF_LOG_LEVEL
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#endif
|
||||
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "CARIBOU_SMI_Utils"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include "smi_utils.h"
|
||||
#include "zf_log/zf_log.h"
|
||||
|
||||
//=========================================================================
|
||||
void smi_utils_set_realtime_priority(int priority_deter)
|
||||
{
|
||||
int ret;
|
||||
|
||||
// We'll operate on the currently running thread.
|
||||
pthread_t this_thread = pthread_self();
|
||||
// struct sched_param is used to store the scheduling priority
|
||||
struct sched_param params;
|
||||
|
||||
// We'll set the priority to the maximum.
|
||||
params.sched_priority = sched_get_priority_max(SCHED_FIFO) - priority_deter;
|
||||
ZF_LOGI("Trying to set thread realtime prio = %d", params.sched_priority);
|
||||
|
||||
// Attempt to set thread real-time priority to the SCHED_FIFO policy
|
||||
ret = pthread_setschedparam(this_thread, SCHED_FIFO, ¶ms);
|
||||
if (ret != 0)
|
||||
{
|
||||
// Print the error
|
||||
ZF_LOGE("Unsuccessful in setting thread realtime prio");
|
||||
return;
|
||||
}
|
||||
// Now verify the change in thread priority
|
||||
int policy = 0;
|
||||
ret = pthread_getschedparam(this_thread, &policy, ¶ms);
|
||||
if (ret != 0)
|
||||
{
|
||||
ZF_LOGE("Couldn't retrieve real-time scheduling paramers");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the correct policy was applied
|
||||
if(policy != SCHED_FIFO)
|
||||
{
|
||||
ZF_LOGE("Scheduling is NOT SCHED_FIFO!");
|
||||
} else {
|
||||
ZF_LOGI("SCHED_FIFO OK");
|
||||
}
|
||||
|
||||
// Print thread scheduling priority
|
||||
ZF_LOGI("Thread priority is %d", params.sched_priority);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
void smi_utils_dump_hex(const void* data, size_t size)
|
||||
{
|
||||
char ascii[17];
|
||||
size_t i, j;
|
||||
ascii[16] = '\0';
|
||||
|
||||
for (i = 0; i < size; ++i)
|
||||
{
|
||||
printf("%02X ", ((unsigned char*)data)[i]);
|
||||
if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~')
|
||||
{
|
||||
ascii[i % 16] = ((unsigned char*)data)[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
ascii[i % 16] = '.';
|
||||
}
|
||||
if ((i+1) % 8 == 0 || i+1 == size)
|
||||
{
|
||||
printf(" ");
|
||||
if ((i+1) % 16 == 0)
|
||||
{
|
||||
printf("| %s \n", ascii);
|
||||
}
|
||||
else if (i+1 == size)
|
||||
{
|
||||
ascii[(i+1) % 16] = '\0';
|
||||
if ((i+1) % 16 <= 8)
|
||||
{
|
||||
printf(" ");
|
||||
}
|
||||
for (j = (i+1) % 16; j < 16; ++j)
|
||||
{
|
||||
printf(" ");
|
||||
}
|
||||
printf("| %s \n", ascii);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
void smi_utils_dump_hex_simple(const void* data, size_t size, size_t delim)
|
||||
{
|
||||
unsigned int temp = 0;
|
||||
for (unsigned int i = 0; i < size; ++i)
|
||||
{
|
||||
temp ++;
|
||||
printf("%02X ", ((unsigned char*)data)[i]);
|
||||
if (delim > 0 && temp > delim)
|
||||
{
|
||||
temp = 0;
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
void smi_utils_dump_bin(const uint8_t* data, size_t size)
|
||||
{
|
||||
char str[16] = {0};
|
||||
|
||||
for (size_t i = 0; i < size; i++)
|
||||
{
|
||||
if (i % 8 == 0) printf("\n");
|
||||
int k = 0;
|
||||
uint8_t b = data[i];
|
||||
for (k = 0; k < 8; k++)
|
||||
{
|
||||
str[k] = (b&0x80)==0?'0':'1';
|
||||
b <<= 1;
|
||||
}
|
||||
str[k] = ' ';
|
||||
printf("%s", str);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
void smi_utils_print_bin(uint32_t v)
|
||||
{
|
||||
char str[48] = {0};
|
||||
int i = 0;
|
||||
for (int k = 0; k < 32; k++)
|
||||
{
|
||||
if (k%8==0) str[i++]=' ';
|
||||
str[i++] = (v&0x80000000)==0?'0':'1';
|
||||
v <<= 1;
|
||||
}
|
||||
printf("%s\n", str);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
int smi_utils_allocate_buffer_vec(uint8_t*** mat, int num_buffers, int buffer_size)
|
||||
{
|
||||
ZF_LOGI("Allocating buffer vectors");
|
||||
(*mat) = (uint8_t**) malloc( num_buffers * sizeof(uint8_t*) );
|
||||
if ((*mat) == NULL)
|
||||
{
|
||||
ZF_LOGE("buffer vector allocation failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset( (*mat), 0, num_buffers * sizeof(uint8_t*) );
|
||||
|
||||
int failed = 0;
|
||||
int i;
|
||||
for (i = 0; i < num_buffers; i++)
|
||||
{
|
||||
(*mat)[i] = (uint8_t*)calloc( buffer_size, sizeof(uint8_t) );
|
||||
if ((*mat)[i] == NULL)
|
||||
{
|
||||
failed = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (failed)
|
||||
{
|
||||
for (int j = 0; j < i; j++)
|
||||
{
|
||||
free((*mat)[j]);
|
||||
}
|
||||
free((*mat));
|
||||
|
||||
ZF_LOGE("buffer (%d) allocation failed", i);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
void smi_utils_release_buffer_vec(uint8_t** mat, int num_buffers, int buffer_size)
|
||||
{
|
||||
ZF_LOGI("Releasing buffer vectors");
|
||||
if (mat == NULL)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < num_buffers; i ++)
|
||||
{
|
||||
if (mat[i] != NULL) free (mat[i]);
|
||||
}
|
||||
|
||||
free(mat);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
int smi_utils_search_offset_in_buffer(uint8_t *buff, int len)
|
||||
{
|
||||
bool succ = false;
|
||||
int off = 0;
|
||||
while (!succ)
|
||||
{
|
||||
if ( (buff[off + 0] & 0xC0) == 0xC0 &&
|
||||
(buff[off + 4] & 0xC0) == 0xC0 &&
|
||||
(buff[off + 8] & 0xC0) == 0xC0 &&
|
||||
(buff[off + 12] & 0xC0) == 0xC0 )
|
||||
return off;
|
||||
off ++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
uint8_t smi_utils_lfsr(uint8_t n)
|
||||
{
|
||||
uint8_t bit = ((n >> 2) ^ (n >> 3)) & 1;
|
||||
return (n >> 1) | (bit << 7);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
double smi_calculate_performance(size_t bytes, struct timeval *old_time, double old_mbps)
|
||||
{
|
||||
struct timeval current_time = {0,0};
|
||||
|
||||
gettimeofday(¤t_time, NULL);
|
||||
|
||||
double elapsed_us = (current_time.tv_sec - old_time->tv_sec) + ((double)(current_time.tv_usec - old_time->tv_usec)) / 1000000.0;
|
||||
double speed_mbps = (double)(bytes * 8) / elapsed_us / 1e6;
|
||||
old_time->tv_sec = current_time.tv_sec;
|
||||
old_time->tv_usec = current_time.tv_usec;
|
||||
return old_mbps * 0.98 + speed_mbps * 0.02;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
unsigned int smi_utils_count_bit(unsigned int x)
|
||||
{
|
||||
x = (x & 0x55555555) + ((x >> 1) & 0x55555555);
|
||||
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
|
||||
x = (x & 0x0F0F0F0F) + ((x >> 4) & 0x0F0F0F0F);
|
||||
x = (x & 0x00FF00FF) + ((x >> 8) & 0x00FF00FF);
|
||||
x = (x & 0x0000FFFF) + ((x >> 16)& 0x0000FFFF);
|
||||
return x;
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
#ifndef __UTILS_H__
|
||||
#define __UTILS_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define TIMING_PERF_SYNC (0)
|
||||
|
||||
#if (TIMING_PERF_SYNC)
|
||||
#define TIMING_PERF_SYNC_VARS \
|
||||
struct timeval tv_pre = {0}; \
|
||||
struct timeval tv_post = {0}; \
|
||||
long long total_samples = 0,last_total_samples = 0; \
|
||||
double time_pre = 0, batch_time = 0, sample_rate = 0; \
|
||||
double time_post = 0, process_time = 0; \
|
||||
double temp_pre; \
|
||||
double num_samples = 0, num_samples_avg = 0;
|
||||
|
||||
#define TIMING_PERF_SYNC_TICK \
|
||||
gettimeofday(&tv_pre, NULL);
|
||||
|
||||
#define TIMING_PERF_SYNC_TOCK \
|
||||
gettimeofday(&tv_post, NULL); \
|
||||
num_samples = (double)(st->read_ret_value) / 4.0; \
|
||||
num_samples_avg = num_samples_avg*0.1 + num_samples*0.9; \
|
||||
temp_pre = tv_pre.tv_sec + ((double)(tv_pre.tv_usec)) / 1e6; \
|
||||
time_post = tv_post.tv_sec + ((double)(tv_post.tv_usec)) / 1e6; \
|
||||
batch_time = temp_pre - time_pre; \
|
||||
sample_rate = sample_rate*0.1 + (num_samples / batch_time) * 0.9; \
|
||||
process_time = process_time*0.1 + (time_post - temp_pre)*0.9; \
|
||||
time_pre = temp_pre; \
|
||||
total_samples += st->read_ret_value; \
|
||||
if ((total_samples - last_total_samples) > 4000000*4) \
|
||||
{ \
|
||||
last_total_samples = total_samples; \
|
||||
ZF_LOGD("sample_rate = %.2f SPS, process_time = %.2f usec" \
|
||||
", num_samples_avg = %.1f", \
|
||||
sample_rate, process_time * 1e6, num_samples_avg); \
|
||||
}
|
||||
#else
|
||||
#define TIMING_PERF_SYNC_VARS
|
||||
#define TIMING_PERF_SYNC_TICK
|
||||
#define TIMING_PERF_SYNC_TOCK
|
||||
#endif
|
||||
|
||||
|
||||
void smi_utils_set_realtime_priority(int priority_deter);
|
||||
void smi_utils_dump_hex(const void* data, size_t size);
|
||||
void smi_utils_dump_hex_simple(const void* data, size_t size, size_t delim);
|
||||
void smi_utils_dump_bin(const uint8_t* data, size_t size);
|
||||
void smi_utils_print_bin(const uint32_t v);
|
||||
int smi_utils_allocate_buffer_vec(uint8_t*** mat, int num_buffers, int buffer_size);
|
||||
void smi_utils_release_buffer_vec(uint8_t** mat, int num_buffers, int buffer_size);
|
||||
int smi_utils_search_offset_in_buffer(uint8_t *buff, int len);
|
||||
uint8_t smi_utils_lfsr(uint8_t n);
|
||||
double smi_calculate_performance(size_t bytes, struct timeval *old_time, double old_mbps);
|
||||
unsigned int smi_utils_count_bit(unsigned int x);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __UTILS_H__
|
|
@ -1,183 +0,0 @@
|
|||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "CARIBOU_SMI_Test"
|
||||
|
||||
#include "zf_log/zf_log.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include <unistd.h>
|
||||
#include <time.h>
|
||||
#include "caribou_smi.h"
|
||||
#include "utils.h"
|
||||
|
||||
caribou_smi_st dev = {0};
|
||||
char program_name[] = "test_caribou_smi.c";
|
||||
|
||||
#define NUM_MS (5)
|
||||
#define NUM_SAMPLES (NUM_MS * 4096)
|
||||
#define SIZE_OF_SAMPLE (4)
|
||||
#define NUM_OF_BUFFERS (2)
|
||||
|
||||
|
||||
//==============================================
|
||||
void swap_little_big (uint32_t *v)
|
||||
{
|
||||
uint8_t b[4];
|
||||
b[0] = (uint8_t) ((*v) >> 24u);
|
||||
b[1] = (uint8_t) ((*v) >> 16u);
|
||||
b[2] = (uint8_t) ((*v) >> 8u);
|
||||
b[3] = (uint8_t) ((*v) >> 0u);
|
||||
*v = *((uint32_t*)(b));
|
||||
}
|
||||
|
||||
//==============================================
|
||||
void print_iq(uint32_t* array, int len)
|
||||
{
|
||||
printf("Values I/Q:\n");
|
||||
for (int i=0; i<len; i++)
|
||||
{
|
||||
unsigned int v = array[i];
|
||||
//swap_little_big (&v);
|
||||
|
||||
int16_t q_val = (v>> 1) & (0x1FFF);
|
||||
int16_t i_val = (v>>17) & (0x1FFF);
|
||||
if (q_val >= 0x1000) q_val-=0x2000;
|
||||
if (i_val >= 0x1000) i_val-=0x2000;
|
||||
float fi = i_val, fq = q_val;
|
||||
float mod = sqrt(fi*fi + fq*fq);
|
||||
float arg = atan2(fq, fi);
|
||||
printf("%08X, %d, %d, %.4f, %.2f\n", v, i_val, q_val, mod, arg);
|
||||
}
|
||||
}
|
||||
|
||||
void caribou_smi_data_event(void *ctx, // The context of the requesting application
|
||||
void *serviced_context, // the context of the session within the app
|
||||
caribou_smi_stream_type_en type, // which type of stream is it? read / write?
|
||||
caribou_smi_event_type_en ev, // the event (start / stop)
|
||||
caribou_smi_channel_en ch, // which channel (900 / 2400)
|
||||
size_t num_samples, // for "read stream only" - number of read data bytes in buffer
|
||||
caribou_smi_sample_complex_int16 *cplx_vec, // for "read" - complex vector of samples to be analyzed
|
||||
// for "write" - complex vector of samples to be written into
|
||||
caribou_smi_sample_meta *metadat_vec, // for "read" - the metadata send by the receiver for each sample
|
||||
// for "write" - the metadata to be written by app for each sample
|
||||
size_t total_length_samples)
|
||||
{
|
||||
if (ev == caribou_smi_event_type_start)
|
||||
{
|
||||
ZF_LOGD("start event: stream batch length: %u samples\n", total_length_samples);
|
||||
return;
|
||||
}
|
||||
else if (ev == caribou_smi_event_type_end)
|
||||
{
|
||||
ZF_LOGD("end event: stream batch length: %u samples\n", total_length_samples);
|
||||
return;
|
||||
}
|
||||
|
||||
switch(type)
|
||||
{
|
||||
//-------------------------------------------------------
|
||||
case caribou_smi_stream_type_read:
|
||||
{
|
||||
ZF_LOGD("data event: stream channel %d, received %u samples\n", ch, num_samples);
|
||||
//print_iq((uint32_t*)buffer, 8);
|
||||
/*for (int i = 0; i< byte_count; i++)
|
||||
{
|
||||
uint8_t dist = (uint8_t)(buffer[i] - last_byte);
|
||||
if ( dist > 1)
|
||||
{
|
||||
err_count ++;
|
||||
float bER = (float)(err_count) / (float)(c);
|
||||
printf("ERR > at %d, dist: %d, V: %d, bER: %.7f\n", c, dist, buffer[i], bER);
|
||||
}
|
||||
|
||||
last_byte = buffer[i];
|
||||
c++;
|
||||
}*/
|
||||
if ( num_samples > 0 )
|
||||
{
|
||||
/*printf("CHUNK %d> %02x %02x %02x %02x...\n", c, buffer[0],
|
||||
buffer[1],
|
||||
buffer[2],
|
||||
buffer[3]);*/
|
||||
/*
|
||||
printf("CUR %d CHUNK > %02x %02x %02x %02x ... %02x %02x %02x %02x\n", c,
|
||||
buffer[0],
|
||||
buffer[1],
|
||||
buffer[2],
|
||||
buffer[3],
|
||||
buffer[byte_count-4],
|
||||
buffer[byte_count-3],
|
||||
buffer[byte_count-2],
|
||||
buffer[byte_count-1]);*/
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
//-------------------------------------------------------
|
||||
case caribou_smi_stream_type_write:
|
||||
{
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
//-------------------------------------------------------
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================
|
||||
void caribou_smi_error_event( void *ctx, caribou_smi_channel_en ch, caribou_smi_error_en err)
|
||||
{
|
||||
ZF_LOGD("Error (from %s) occured in channel %d, err# %d (%s)\n", (char*)ctx, ch, err, caribou_smi_get_error_string(err));
|
||||
}
|
||||
|
||||
//==============================================
|
||||
int main_single_read()
|
||||
{
|
||||
int read_count = 4096;
|
||||
uint32_t buffer[read_count];
|
||||
char* b8 = (char*)buffer;
|
||||
|
||||
caribou_smi_init(&dev, caribou_smi_error_event, program_name);
|
||||
|
||||
caribou_smi_timeout_read(&dev, address, b8, read_count*sizeof(uint32_t), 1000);
|
||||
smi_utils_dump_hex(b8, read_count*sizeof(uint32_t));
|
||||
print_iq(buffer, read_count);
|
||||
caribou_smi_close (&dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//==============================================
|
||||
int main_stream_read()
|
||||
{
|
||||
caribou_smi_init(&dev, caribou_smi_error_event, program_name);
|
||||
int stream_id = caribou_smi_setup_stream(&dev,
|
||||
caribou_smi_stream_type_read,
|
||||
channel,
|
||||
caribou_smi_data_event,
|
||||
NULL);
|
||||
|
||||
sleep(1);
|
||||
caribou_smi_run_pause_stream (&dev, stream_id, 1);
|
||||
printf("Listening on stream_id = %d\n", stream_id);
|
||||
printf("Press ENTER to exit...\n");
|
||||
getchar();
|
||||
|
||||
printf("ENTER pressed...\n");
|
||||
|
||||
//caribou_smi_run_pause_stream (&dev, stream_id, 0);
|
||||
caribou_smi_destroy_stream(&dev, stream_id);
|
||||
caribou_smi_close (&dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//==============================================
|
||||
int main()
|
||||
{
|
||||
main_stream_read();
|
||||
return 0;
|
||||
}
|
|
@ -1,252 +0,0 @@
|
|||
#include "cariboulite.h"
|
||||
#include "cariboulite_setup.h"
|
||||
#include "cariboulite_radio.h"
|
||||
|
||||
// ----------------------
|
||||
// INTERNAL DATA TYPES
|
||||
// ----------------------
|
||||
typedef struct
|
||||
{
|
||||
cariboulite_lib_version_st lib_version;
|
||||
cariboulite_signal_handler sighandler;
|
||||
void* sig_context;
|
||||
bool initialized;
|
||||
cariboulite_log_level_en log_level;
|
||||
int signal_shown;
|
||||
|
||||
} cariboulite_api_context_st;
|
||||
|
||||
// ----------------------
|
||||
// STATIC VARIABLES
|
||||
// ----------------------
|
||||
CARIBOULITE_CONFIG_STATIC_DEFAULT(sys);
|
||||
static cariboulite_api_context_st ctx = {
|
||||
.lib_version = {0},
|
||||
.sighandler = NULL,
|
||||
.sig_context = NULL,
|
||||
.initialized = false,
|
||||
.log_level = cariboulite_log_level_verbose,
|
||||
.signal_shown = 0,
|
||||
};
|
||||
|
||||
//=============================================================================
|
||||
static void internal_sighandler( struct sys_st_t *sys,
|
||||
void* context,
|
||||
int signal_number,
|
||||
siginfo_t *si)
|
||||
{
|
||||
if (ctx.signal_shown != signal_number)
|
||||
{
|
||||
fprintf(stderr, "Received signal %d", signal_number);
|
||||
ctx.signal_shown = signal_number;
|
||||
}
|
||||
|
||||
if (ctx.sighandler) ctx.sighandler(ctx.sig_context, signal_number, si);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
bool cariboulite_detect_connected_board(cariboulite_version_en *hw_ver, char* name, char *uuid)
|
||||
{
|
||||
hat_board_info_st hat;
|
||||
if (hat_detect_board(&hat) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (hat.numeric_product_id)
|
||||
{
|
||||
case system_type_cariboulite_full:
|
||||
if (hw_ver) *hw_ver = cariboulite_full;
|
||||
if (name) sprintf(name, "CaribouLite 6G");
|
||||
break;
|
||||
|
||||
case system_type_cariboulite_ism:
|
||||
if (hw_ver) *hw_ver = cariboulite_ism;
|
||||
if (name) sprintf(name, "CaribouLite ISM");
|
||||
break;
|
||||
|
||||
case system_type_unknown:
|
||||
if (hw_ver) *hw_ver = cariboulite_unknown;
|
||||
if (name) sprintf(name, "CaribouLite Unknown");
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (uuid) sprintf(uuid, "%s", hat.product_uuid);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
int cariboulite_init(bool force_fpga_prog, cariboulite_log_level_en log_lvl)
|
||||
{
|
||||
sys.force_fpga_reprogramming = force_fpga_prog;
|
||||
|
||||
cariboulite_set_log_level(log_lvl);
|
||||
if (cariboulite_init_driver(&sys, NULL)!=0)
|
||||
{
|
||||
fprintf(stderr, "CaribouLite driver init failed, terminating...");
|
||||
return -1;
|
||||
}
|
||||
cariboulite_setup_signal_handler (&sys, internal_sighandler, signal_handler_op_override, &sys);
|
||||
ctx.initialized = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void cariboulite_close(void)
|
||||
{
|
||||
if (!ctx.initialized) return;
|
||||
ctx.initialized = false;
|
||||
cariboulite_release_driver(&sys);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
bool cariboulite_is_initialized(void)
|
||||
{
|
||||
return ctx.initialized;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void cariboulite_register_signal_handler ( cariboulite_signal_handler handler,
|
||||
void *context)
|
||||
{
|
||||
ctx.sighandler = handler;
|
||||
ctx.sig_context = context;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
void cariboulite_get_lib_version(cariboulite_lib_version_st* v)
|
||||
{
|
||||
cariboulite_lib_version(v);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
unsigned int cariboulite_get_sn()
|
||||
{
|
||||
uint32_t sn = 0;
|
||||
int count = 0;
|
||||
cariboulite_get_serial_number(&sys, &sn, &count);
|
||||
return sn;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
cariboulite_radio_state_st* cariboulite_get_radio(cariboulite_channel_en ch)
|
||||
{
|
||||
return cariboulite_get_radio_handle(&sys, ch);
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
cariboulite_version_en cariboulite_get_version()
|
||||
{
|
||||
system_type_en type = cariboulite_get_system_type(&sys);
|
||||
switch(type)
|
||||
{
|
||||
case cariboulite_full: return cariboulite_full; break;
|
||||
case cariboulite_ism: return cariboulite_ism; break;
|
||||
case system_type_unknown:
|
||||
default: return cariboulite_unknown; break;
|
||||
}
|
||||
return cariboulite_unknown;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
bool cariboulite_frequency_available(cariboulite_channel_en ch, float freq_hz)
|
||||
{
|
||||
if (ch == cariboulite_channel_s1g)
|
||||
{
|
||||
return (freq_hz >= CARIBOULITE_S1G_MIN1 && freq_hz <= CARIBOULITE_S1G_MAX1 ||
|
||||
freq_hz >= CARIBOULITE_S1G_MIN2 && freq_hz <= CARIBOULITE_S1G_MAX2);
|
||||
}
|
||||
else if (ch == cariboulite_channel_hif)
|
||||
{
|
||||
cariboulite_version_en ver = cariboulite_get_version();
|
||||
if (ver == cariboulite_full)
|
||||
{
|
||||
return (freq_hz >= CARIBOULITE_6G_MIN && freq_hz <= CARIBOULITE_6G_MAX);
|
||||
}
|
||||
else if (ver == cariboulite_ism)
|
||||
{
|
||||
return (freq_hz >= CARIBOULITE_2G4_MIN && freq_hz <= CARIBOULITE_2G4_MAX);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
int cariboulite_get_num_frequency_ranges(cariboulite_channel_en ch)
|
||||
{
|
||||
if (ch == cariboulite_channel_s1g) return 2;
|
||||
else if (ch == cariboulite_channel_hif) return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
int cariboulite_get_frequency_limits(cariboulite_channel_en ch, float *freq_low, float *freq_hi, int* num_ranges)
|
||||
{
|
||||
if (ch == cariboulite_channel_s1g)
|
||||
{
|
||||
freq_low[0] = CARIBOULITE_S1G_MIN1;
|
||||
freq_hi[0] = CARIBOULITE_S1G_MAX1;
|
||||
freq_low[1] = CARIBOULITE_S1G_MIN2;
|
||||
freq_hi[1] = CARIBOULITE_S1G_MAX2;
|
||||
if (num_ranges) *num_ranges = 2;
|
||||
}
|
||||
else if (ch == cariboulite_channel_hif)
|
||||
{
|
||||
cariboulite_version_en ver = cariboulite_get_version();
|
||||
if (ver == cariboulite_full)
|
||||
{
|
||||
freq_low[0] = CARIBOULITE_6G_MIN;
|
||||
freq_hi[0] = CARIBOULITE_6G_MAX;
|
||||
if (num_ranges) *num_ranges = 1;
|
||||
}
|
||||
else if (ver == cariboulite_ism)
|
||||
{
|
||||
freq_low[0] = CARIBOULITE_2G4_MIN;
|
||||
freq_hi[0] = CARIBOULITE_2G4_MAX;
|
||||
if (num_ranges) *num_ranges = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
int cariboulite_get_channel_name(cariboulite_channel_en ch, char* name, size_t max_len)
|
||||
{
|
||||
if (ch == cariboulite_channel_s1g)
|
||||
{
|
||||
snprintf(name, max_len-1, "CaribouLite S1G");
|
||||
return 0;
|
||||
}
|
||||
else if (ch == cariboulite_channel_hif)
|
||||
{
|
||||
cariboulite_version_en ver = cariboulite_get_version();
|
||||
if (ver == cariboulite_full)
|
||||
{
|
||||
snprintf(name, max_len-1, "CaribouLite 6GHz");
|
||||
return 0;
|
||||
}
|
||||
else if (ver == cariboulite_ism)
|
||||
{
|
||||
snprintf(name, max_len-1, "CaribouLite 2.4GHz");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
int cariboulite_flush_pipeline()
|
||||
{
|
||||
// request the smi driver to flush its drivers fifo
|
||||
return caribou_smi_flush_fifo(&sys.smi);
|
||||
}
|
|
@ -1,228 +0,0 @@
|
|||
/**
|
||||
* @file cariboulite.h
|
||||
* @author David Michaeli
|
||||
* @date September 2023
|
||||
* @brief Main Init/Close API
|
||||
*
|
||||
* A high level management API for CaribouLite
|
||||
*/
|
||||
|
||||
#ifndef __CARIBOULITE_H__
|
||||
#define __CARIBOULITE_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <signal.h>
|
||||
#include "cariboulite_radio.h"
|
||||
|
||||
/**
|
||||
* @brief library version
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int major_version;
|
||||
int minor_version;
|
||||
int revision;
|
||||
} cariboulite_lib_version_st;
|
||||
|
||||
/**
|
||||
* @brief Log Level
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
cariboulite_log_level_verbose, /**< Full */
|
||||
cariboulite_log_level_info, /**< partial - no debug */
|
||||
cariboulite_log_level_none, /**< none - errors only*/
|
||||
} cariboulite_log_level_en;
|
||||
|
||||
/**
|
||||
* @brief System Type (version)
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
cariboulite_unknown = 0,
|
||||
cariboulite_full = 1,
|
||||
cariboulite_ism = 2,
|
||||
} cariboulite_version_en;
|
||||
|
||||
/**
|
||||
* @brief custom signal handler
|
||||
*/
|
||||
typedef void (*cariboulite_signal_handler)( void* context, // custom context - can be a higher level app class
|
||||
int signal_number, // the signal number
|
||||
siginfo_t *si);
|
||||
|
||||
/**
|
||||
* @brief check if board connected (without initing it)
|
||||
*
|
||||
* This function attempts to read the HAT information and checks if a
|
||||
* CaribouLite is registerred in the OS. If so gets its details.
|
||||
* this fucntion is good to check whether the board is mounted without the need
|
||||
* to initialize it. Preferably this is a simple check before we start working
|
||||
* - kind of a sanity check.
|
||||
*
|
||||
* @param hw_ver the type of HW (full / ism) as ENUM (nullable is not needed)
|
||||
* @param name the type of HW in textual manner - preallocate the string (at least 32 bytes)
|
||||
* (nullable is not needed)
|
||||
* @uuid uuid if this is needed, the user needs to provide a preallocated string 64 byte typical
|
||||
* (nullable if not needed)
|
||||
* @return true - when board was detected
|
||||
* false - board was not detected and then other parameters will not be valid
|
||||
*/
|
||||
bool cariboulite_detect_connected_board(cariboulite_version_en *hw_ver, char* name, char *guid);
|
||||
|
||||
/**
|
||||
* @brief initialize the system
|
||||
*
|
||||
* This function performs fully entry initialization of the system and
|
||||
* a short self-test sequence to communication and check all the components
|
||||
* respond.
|
||||
*
|
||||
* @param force_fpga_prog force the FPGA to program (even if it is already programmed
|
||||
* this will have a penalty of around 3 seconds on init. If false
|
||||
* and the FPGA is already programmed (typically when this is not
|
||||
* the first time a program has run since last boot session), the program
|
||||
* will not rerun the FPGA programming process.
|
||||
* @param log_lvl the logging level according to 'cariboulite_log_level_en'
|
||||
* @return success / fail codes according to "cariboulite_errors_en"
|
||||
*/
|
||||
int cariboulite_init(bool force_fpga_prog, cariboulite_log_level_en log_lvl);
|
||||
|
||||
/**
|
||||
* @brief Release resources
|
||||
*
|
||||
* Releasing resources taken during init and program execution. Should be
|
||||
* called at the end of the session.
|
||||
*
|
||||
* @param sys a pre-allocated device handle structure
|
||||
*/
|
||||
void cariboulite_close(void);
|
||||
|
||||
/**
|
||||
* @brief Returns init status
|
||||
*
|
||||
* checks whether the driver is initialized
|
||||
*
|
||||
* @return boolean result (true = initialized)
|
||||
*/
|
||||
bool cariboulite_is_initialized(void);
|
||||
|
||||
/**
|
||||
* @brief Register an explicit linux signal handler in the application level
|
||||
*
|
||||
* If a linux signal occures it is caught by the system. The user may want to be
|
||||
* notified on the signal in the app level. This is done by registering a handler
|
||||
* function with this fucntion.
|
||||
*
|
||||
* @param sys a pre-allocated device handle structure
|
||||
* @param handler a handler function with the following prototype:
|
||||
* void (*signal_handler)( void* context, // custom context - can be a higher level app class
|
||||
* int signal_number, // the signal number
|
||||
* siginfo_t *si);
|
||||
* @param context this void* pointer may contain an app specific handle to be passed to the explicit signal handler.
|
||||
* @return always 0 (success)
|
||||
*/
|
||||
void cariboulite_register_signal_handler ( cariboulite_signal_handler handler,
|
||||
void *context);
|
||||
|
||||
/**
|
||||
* @brief Get lib version
|
||||
*
|
||||
* @param v a struct containing the version information
|
||||
*/
|
||||
void cariboulite_get_lib_version(cariboulite_lib_version_st* v);
|
||||
|
||||
/**
|
||||
* @brief Get board serial number
|
||||
*
|
||||
* @return board serial number (32 bit)
|
||||
*/
|
||||
unsigned int cariboulite_get_sn();
|
||||
|
||||
/**
|
||||
* @brief Getting the used radio handle
|
||||
*
|
||||
* After initializing the drivers, a radio device is created and stored in
|
||||
* the device driver main struct. To manipulate the radio features, this radio
|
||||
* handle (pointer) needs to be obtained by the user. This handle is normally
|
||||
* passed to the radio manipulation functions in "cariboulite_radio.h"
|
||||
*
|
||||
* @param type the radio channel (6G/2.4G or ISM)
|
||||
* @return the handle of the channel, or NULL if invalid channel id
|
||||
*/
|
||||
cariboulite_radio_state_st* cariboulite_get_radio(cariboulite_channel_en ch);
|
||||
|
||||
/**
|
||||
* @brief Getting CaribouLite version
|
||||
*
|
||||
* Returns the version of the hardware - ISM / 6G / Unknown
|
||||
* ISM has two channels - 900MHz and 2.4 GHz
|
||||
* 6G has two channels - 900MHz and 6000MHz
|
||||
*
|
||||
* @return according to cariboulite_version_en
|
||||
*/
|
||||
cariboulite_version_en cariboulite_get_version(void);
|
||||
|
||||
/**
|
||||
* @brief Getting frequency availability
|
||||
*
|
||||
* Given a certain frequency (in Hz) checking if that frequency is available in the
|
||||
* connected hardware
|
||||
*
|
||||
* @return true / false
|
||||
*/
|
||||
bool cariboulite_frequency_available(cariboulite_channel_en ch, float freq_hz);
|
||||
|
||||
/**
|
||||
* @brief Getting the number frequency ranges
|
||||
*
|
||||
* Each channel has its frequency capabilities. in the ISM channel there are 2
|
||||
* ranges, while in the HiF there is a single range. This function returns the number
|
||||
* of available ranges per channel. To get the actual limits use 'cariboulite_get_frequency_limits'
|
||||
*
|
||||
* @return number of ranges or -1 if the channel doesn't exist
|
||||
*/
|
||||
int cariboulite_get_num_frequency_ranges(cariboulite_channel_en ch);
|
||||
|
||||
/**
|
||||
* @brief Getting frequency ranges limits
|
||||
*
|
||||
* freq_low and freq_hi need to be pre-allocated according to 'cariboulite_get_num_frequency_ranges'
|
||||
* then all the minimum values will be in the freq_low list and the corresponding max values will be in
|
||||
* freq_hi. All frequencies in Hz
|
||||
* num_ranges returns the number of written ranges
|
||||
*
|
||||
* @return 0 for success or -1 if channel is wrong
|
||||
*/
|
||||
int cariboulite_get_frequency_limits(cariboulite_channel_en ch, float *freq_low, float *freq_hi, int* num_ranges);
|
||||
|
||||
/**
|
||||
* @brief Get channel name
|
||||
*
|
||||
* Returns the string name of a channel
|
||||
*
|
||||
* @param ch the chosen channel
|
||||
* @param name a pre-allocated char array
|
||||
* @param max_len the size of the pre-allocated char array
|
||||
* @return 0 (success) or -1 (failed - when channel is incorrect)
|
||||
*/
|
||||
int cariboulite_get_channel_name(cariboulite_channel_en ch, char* name, size_t max_len);
|
||||
|
||||
/**
|
||||
* @brief Flush driver FIFO
|
||||
*
|
||||
* Removing all items in the pipeline (drivers kfifo)
|
||||
*
|
||||
* @return 0 (success) or -1 (failed - when channel is incorrect)
|
||||
*/
|
||||
int cariboulite_flush_pipeline(void);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif // __CARIBOULITE_H__
|
|
@ -1,93 +0,0 @@
|
|||
#ifndef __CARIBOULITE_CONFIG_DEFAULT_H__
|
||||
#define __CARIBOULITE_CONFIG_DEFAULT_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// PINOUT SPI
|
||||
#define CARIBOULITE_SPI_DEV 1
|
||||
#define CARIBOULITE_MOSI 20
|
||||
#define CARIBOULITE_SCK 21
|
||||
#define CARIBOULITE_MISO 19
|
||||
|
||||
// PINOUT FPGA
|
||||
#define CARIBOULITE_FPGA_SPI_CHANNEL 0
|
||||
#define CARIBOULITE_FPGA_SS 18
|
||||
#define CARIBOULITE_FPGA_CDONE 27
|
||||
#define CARIBOULITE_FPGA_CRESET 26
|
||||
#define CARIBOULITE_FPGA_SOFT_RESET 4
|
||||
|
||||
// PINOUT AT86 - AT86RF215
|
||||
#define CARIBOULITE_MODEM_SPI_CHANNEL 1
|
||||
#define CARIBOULITE_MODEM_SS 17
|
||||
#define CARIBOULITE_MODEM_IRQ 22
|
||||
#define CARIBOULITE_MODEM_RESET 23
|
||||
|
||||
// PINOUT MIXER - RFFC5072
|
||||
#define CARIBOULITE_MIXER_SPI_CHANNEL 2
|
||||
#define CARIBOULITE_MIXER_SS 16
|
||||
#define CARIBOULITE_MIXER_RESET 5
|
||||
|
||||
//=======================================================================================
|
||||
// SYSTEM DEFINITIONS & CONFIGURATIONS
|
||||
//=======================================================================================
|
||||
#define CARIBOULITE_CONFIG_DEFAULT(a) \
|
||||
sys_st(a)={ \
|
||||
.board_info = {0}, \
|
||||
.spi_dev = \
|
||||
{ \
|
||||
.miso = CARIBOULITE_MISO, \
|
||||
.mosi = CARIBOULITE_MOSI, \
|
||||
.sck = CARIBOULITE_SCK, \
|
||||
.initialized = 0, \
|
||||
}, \
|
||||
.smi = \
|
||||
{ \
|
||||
.initialized = 0, \
|
||||
}, \
|
||||
.fpga = \
|
||||
{ \
|
||||
.reset_pin = CARIBOULITE_FPGA_CRESET, \
|
||||
.soft_reset_pin = CARIBOULITE_FPGA_SOFT_RESET, \
|
||||
.cs_pin = CARIBOULITE_FPGA_SS, \
|
||||
.spi_dev = CARIBOULITE_SPI_DEV, \
|
||||
.spi_channel = CARIBOULITE_FPGA_SPI_CHANNEL, \
|
||||
.prog_dev = \
|
||||
{ \
|
||||
.cs_pin = CARIBOULITE_FPGA_SS, \
|
||||
.cdone_pin = CARIBOULITE_FPGA_CDONE, \
|
||||
.reset_pin = CARIBOULITE_FPGA_CRESET, \
|
||||
}, \
|
||||
.initialized = 0, \
|
||||
}, \
|
||||
.modem = \
|
||||
{ \
|
||||
.reset_pin = CARIBOULITE_MODEM_RESET, \
|
||||
.irq_pin = CARIBOULITE_MODEM_IRQ, \
|
||||
.cs_pin = CARIBOULITE_MODEM_SS, \
|
||||
.spi_dev = CARIBOULITE_SPI_DEV, \
|
||||
.spi_channel = CARIBOULITE_MODEM_SPI_CHANNEL, \
|
||||
.initialized = 0, \
|
||||
.override_cal = true, \
|
||||
}, \
|
||||
.mixer = \
|
||||
{ \
|
||||
.cs_pin = CARIBOULITE_MIXER_SS, \
|
||||
.reset_pin = CARIBOULITE_MIXER_RESET, \
|
||||
.ref_freq_hz = 32e6, \
|
||||
.initialized = 0, \
|
||||
}, \
|
||||
.reset_fpga_on_startup = 1, \
|
||||
.system_status = sys_status_unintialized, \
|
||||
}
|
||||
|
||||
|
||||
#define CARIBOULITE_CONFIG_STATIC_DEFAULT(a) \
|
||||
static CARIBOULITE_CONFIG_DEFAULT(a)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //__CARIBOULITE_CONFIG_DEFAULT_H__
|
|
@ -1,113 +0,0 @@
|
|||
/*
|
||||
* This file was automatically generated using the 'generate_bin_blob' tool
|
||||
* Modification of this file is not recommanded - please re-generate it
|
||||
* as needed and embed in the code library.
|
||||
*/
|
||||
|
||||
#ifndef __cariboulite_dtbo_h__
|
||||
#define __cariboulite_dtbo_h__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
/*
|
||||
* Time tagging of the module through the 'struct tm' structure
|
||||
* Date: 2023-02-14
|
||||
* Time: 10:19:31
|
||||
*/
|
||||
struct tm cariboulite_dtbo_date_time = {
|
||||
.tm_sec = 31,
|
||||
.tm_min = 19,
|
||||
.tm_hour = 10,
|
||||
.tm_mday = 14,
|
||||
.tm_mon = 1, /* +1 */
|
||||
.tm_year = 123, /* +1900 */
|
||||
};
|
||||
|
||||
/*
|
||||
* Data blob of variable cariboulite_dtbo:
|
||||
* Size: 1100 bytes
|
||||
* Original filename: ./cariboulite.dtbo
|
||||
*/
|
||||
uint8_t cariboulite_dtbo[] = {
|
||||
0xD0, 0x0D, 0xFE, 0xED, 0x00, 0x00, 0x04, 0x4C, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x03, 0xD0,
|
||||
0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x03, 0x98, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x62, 0x72, 0x63, 0x6D,
|
||||
0x2C, 0x62, 0x63, 0x6D, 0x32, 0x38, 0x33, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x66, 0x72, 0x61, 0x67, 0x6D, 0x65, 0x6E, 0x74, 0x40, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
|
||||
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0B, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01,
|
||||
0x5F, 0x5F, 0x6F, 0x76, 0x65, 0x72, 0x6C, 0x61, 0x79, 0x5F, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x73, 0x6D, 0x69, 0x5F, 0x64, 0x65, 0x76, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x15,
|
||||
0x00, 0x00, 0x00, 0x00, 0x62, 0x72, 0x63, 0x6D, 0x2C, 0x62, 0x63, 0x6D, 0x32, 0x38, 0x33, 0x35,
|
||||
0x2D, 0x73, 0x6D, 0x69, 0x2D, 0x64, 0x65, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
|
||||
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x12, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x03,
|
||||
0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x1D, 0x6F, 0x6B, 0x61, 0x79, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01,
|
||||
0x66, 0x72, 0x61, 0x67, 0x6D, 0x65, 0x6E, 0x74, 0x40, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
|
||||
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0B, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01,
|
||||
0x5F, 0x5F, 0x6F, 0x76, 0x65, 0x72, 0x6C, 0x61, 0x79, 0x5F, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x03,
|
||||
0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x24, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x00,
|
||||
0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x1D, 0x6F, 0x6B, 0x61, 0x79,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01,
|
||||
0x66, 0x72, 0x61, 0x67, 0x6D, 0x65, 0x6E, 0x74, 0x40, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
|
||||
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x0B, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01,
|
||||
0x5F, 0x5F, 0x6F, 0x76, 0x65, 0x72, 0x6C, 0x61, 0x79, 0x5F, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x73, 0x6D, 0x69, 0x5F, 0x70, 0x69, 0x6E, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
|
||||
0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03,
|
||||
0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09,
|
||||
0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x0D,
|
||||
0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x19,
|
||||
0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x05,
|
||||
0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05,
|
||||
0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05,
|
||||
0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05,
|
||||
0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x54,
|
||||
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04,
|
||||
0x00, 0x00, 0x00, 0x5E, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02,
|
||||
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x5F, 0x5F, 0x73, 0x79, 0x6D, 0x62, 0x6F, 0x6C,
|
||||
0x73, 0x5F, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x66,
|
||||
0x2F, 0x66, 0x72, 0x61, 0x67, 0x6D, 0x65, 0x6E, 0x74, 0x40, 0x32, 0x2F, 0x5F, 0x5F, 0x6F, 0x76,
|
||||
0x65, 0x72, 0x6C, 0x61, 0x79, 0x5F, 0x5F, 0x2F, 0x73, 0x6D, 0x69, 0x5F, 0x70, 0x69, 0x6E, 0x73,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x5F, 0x5F, 0x66, 0x69,
|
||||
0x78, 0x75, 0x70, 0x73, 0x5F, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x15,
|
||||
0x00, 0x00, 0x00, 0x6F, 0x2F, 0x66, 0x72, 0x61, 0x67, 0x6D, 0x65, 0x6E, 0x74, 0x40, 0x30, 0x3A,
|
||||
0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x3A, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
|
||||
0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x73, 0x2F, 0x66, 0x72, 0x61, 0x67, 0x6D, 0x65, 0x6E,
|
||||
0x74, 0x40, 0x30, 0x2F, 0x5F, 0x5F, 0x6F, 0x76, 0x65, 0x72, 0x6C, 0x61, 0x79, 0x5F, 0x5F, 0x2F,
|
||||
0x73, 0x6D, 0x69, 0x5F, 0x64, 0x65, 0x76, 0x3A, 0x73, 0x6D, 0x69, 0x5F, 0x68, 0x61, 0x6E, 0x64,
|
||||
0x6C, 0x65, 0x3A, 0x30, 0x00, 0x2F, 0x66, 0x72, 0x61, 0x67, 0x6D, 0x65, 0x6E, 0x74, 0x40, 0x31,
|
||||
0x3A, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x3A, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
|
||||
0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x77, 0x2F, 0x66, 0x72, 0x61, 0x67, 0x6D, 0x65, 0x6E,
|
||||
0x74, 0x40, 0x32, 0x3A, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x3A, 0x30, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x5F, 0x5F, 0x6C, 0x6F, 0x63, 0x61, 0x6C, 0x5F,
|
||||
0x66, 0x69, 0x78, 0x75, 0x70, 0x73, 0x5F, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x66, 0x72, 0x61, 0x67, 0x6D, 0x65, 0x6E, 0x74, 0x40, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0x5F, 0x5F, 0x6F, 0x76, 0x65, 0x72, 0x6C, 0x61, 0x79, 0x5F, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x03,
|
||||
0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
|
||||
0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x09,
|
||||
0x63, 0x6F, 0x6D, 0x70, 0x61, 0x74, 0x69, 0x62, 0x6C, 0x65, 0x00, 0x74, 0x61, 0x72, 0x67, 0x65,
|
||||
0x74, 0x00, 0x73, 0x6D, 0x69, 0x5F, 0x68, 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x00, 0x73, 0x74, 0x61,
|
||||
0x74, 0x75, 0x73, 0x00, 0x70, 0x69, 0x6E, 0x63, 0x74, 0x72, 0x6C, 0x2D, 0x6E, 0x61, 0x6D, 0x65,
|
||||
0x73, 0x00, 0x70, 0x69, 0x6E, 0x63, 0x74, 0x72, 0x6C, 0x2D, 0x30, 0x00, 0x62, 0x72, 0x63, 0x6D,
|
||||
0x2C, 0x70, 0x69, 0x6E, 0x73, 0x00, 0x62, 0x72, 0x63, 0x6D, 0x2C, 0x66, 0x75, 0x6E, 0x63, 0x74,
|
||||
0x69, 0x6F, 0x6E, 0x00, 0x62, 0x72, 0x63, 0x6D, 0x2C, 0x70, 0x75, 0x6C, 0x6C, 0x00, 0x70, 0x68,
|
||||
0x61, 0x6E, 0x64, 0x6C, 0x65, 0x00, 0x73, 0x6D, 0x69, 0x5F, 0x70, 0x69, 0x6E, 0x73, 0x00, 0x73,
|
||||
0x6F, 0x63, 0x00, 0x73, 0x6D, 0x69, 0x00, 0x67, 0x70, 0x69, 0x6F, 0x00,
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __cariboulite_dtbo_h__
|
|
@ -1,10 +0,0 @@
|
|||
#ifndef ZF_LOG_LEVEL
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#endif
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "CARIBOULITE Events"
|
||||
#include "zf_log/zf_log.h"
|
||||
|
||||
#include "cariboulite.h"
|
||||
#include "cariboulite_events.h"
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
#ifndef __CARIBOULITE_EVENTS_H__
|
||||
#define __CARIBOULITE_EVENTS_H__
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "caribou_smi/caribou_smi.h"
|
||||
|
||||
void caribou_smi_error_event(caribou_smi_channel_en channel, void* context);
|
||||
void caribou_smi_rx_data_event(caribou_smi_channel_en channel, caribou_smi_sample_complex_int16 *cplx_vec, size_t num_samples_in_vec, void* context);
|
||||
size_t caribou_smi_tx_data_event(caribou_smi_channel_en channel, caribou_smi_sample_complex_int16 *cplx_vec, size_t *num_samples_in_vec, void* context);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __CARIBOULITE_EVENTS_H__
|
|
@ -1,115 +0,0 @@
|
|||
#ifndef __CARIBOULITE_INT_H__
|
||||
#define __CARIBOULITE_INT_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "cariboulite_config_default.h"
|
||||
|
||||
#include <signal.h>
|
||||
#include <linux/limits.h> // for file system path max length
|
||||
|
||||
#include "hat/hat.h"
|
||||
#include "io_utils/io_utils.h"
|
||||
#include "io_utils/io_utils_spi.h"
|
||||
#include "io_utils/io_utils_sys_info.h"
|
||||
#include "rffc507x/rffc507x.h"
|
||||
#include "at86rf215/at86rf215.h"
|
||||
|
||||
#include "caribou_programming/caribou_prog.h"
|
||||
#include "caribou_fpga/caribou_fpga.h"
|
||||
#include "caribou_smi/caribou_smi.h"
|
||||
|
||||
#include "cariboulite_radio.h"
|
||||
|
||||
// GENERAL SETTINGS
|
||||
struct sys_st_t;
|
||||
|
||||
typedef void (*signal_handler)( struct sys_st_t *sys, // the current cariboulite low-level management struct
|
||||
void* context, // custom context - can be a higher level app class
|
||||
int signal_number, // the signal number
|
||||
siginfo_t *si);
|
||||
|
||||
typedef enum
|
||||
{
|
||||
signal_handler_op_last = 0, // The curtom sighandler operates (if present) after the default sig handler
|
||||
signal_handler_op_first = 1, // The curtom sighandler operates (if present) before the default sig handler
|
||||
signal_handler_op_override = 2, // The curtom sighandler operates (if present) instead of the default sig handler
|
||||
} signal_handler_operation_en;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
system_type_unknown = 0,
|
||||
system_type_cariboulite_full = 1,
|
||||
system_type_cariboulite_ism = 2,
|
||||
} system_type_en;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
cariboulite_ext_ref_src_modem = 0,
|
||||
cariboulite_ext_ref_src_connector = 1,
|
||||
cariboulite_ext_ref_src_txco = 2,
|
||||
cariboulite_ext_ref_src_na = 3, // not applicable
|
||||
} cariboulite_ext_ref_src_en;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
sys_status_unintialized = 0,
|
||||
sys_status_minimal_init = 1,
|
||||
sys_status_full_init = 2,
|
||||
} sys_status_en;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
cariboulite_ext_ref_src_en src;
|
||||
double freq_hz;
|
||||
} cariboulite_ext_ref_settings_st;
|
||||
|
||||
typedef struct sys_st_t
|
||||
{
|
||||
// board information
|
||||
hat_board_info_st board_info;
|
||||
system_type_en sys_type;
|
||||
|
||||
// SoC level
|
||||
io_utils_spi_st spi_dev;
|
||||
caribou_smi_st smi;
|
||||
//ustimer_t timer;
|
||||
|
||||
// Peripheral chips
|
||||
caribou_fpga_st fpga;
|
||||
at86rf215_st modem;
|
||||
cariboulite_ext_ref_settings_st ext_ref_settings;
|
||||
rffc507x_st mixer;
|
||||
|
||||
// Configuration
|
||||
int reset_fpga_on_startup;
|
||||
int force_fpga_reprogramming;
|
||||
int fpga_config_resistor_state;
|
||||
char firmware_path_operational[PATH_MAX];
|
||||
char firmware_path_testing[PATH_MAX];
|
||||
|
||||
// Radios
|
||||
cariboulite_radio_state_st radio_low;
|
||||
cariboulite_radio_state_st radio_high;
|
||||
|
||||
// Signals
|
||||
signal_handler signal_cb;
|
||||
void* singal_cb_context;
|
||||
signal_handler_operation_en sig_op;
|
||||
|
||||
// Management
|
||||
caribou_fpga_versions_st fpga_versions;
|
||||
uint8_t fpga_error_status;
|
||||
int fpga_config_res_state;
|
||||
// Initialization
|
||||
sys_status_en system_status;
|
||||
} sys_st;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif // __CARIBOULITE_INT_H__
|
|
@ -1,626 +0,0 @@
|
|||
/**
|
||||
* @file cariboulite_radio.h
|
||||
* @author David Michaeli
|
||||
* @date September 2023
|
||||
* @brief System level radio API
|
||||
*
|
||||
* Radio device implementation exposing TxRx API
|
||||
*/
|
||||
#ifndef __CARIBOULABS_RADIO_H__
|
||||
#define __CARIBOULABS_RADIO_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* @brief Radio channel direction
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
cariboulite_channel_dir_rx = 0,
|
||||
cariboulite_channel_dir_tx = 1,
|
||||
} cariboulite_channel_dir_en;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
cariboulite_channel_s1g = 0,
|
||||
cariboulite_channel_hif = 1,
|
||||
} cariboulite_channel_en;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
cariboulite_ext_ref_off = 0,
|
||||
cariboulite_ext_ref_26mhz = 26,
|
||||
cariboulite_ext_ref_32mhz = 32,
|
||||
} cariboulite_ext_ref_freq_en;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
cariboulite_radio_cmd_nop = 0x0, // No operation (dummy)
|
||||
cariboulite_radio_cmd_sleep = 0x1, // Go to sleep
|
||||
cariboulite_radio_state_cmd_trx_off = 0x2, // Transceiver off, SPI active
|
||||
cariboulite_radio_state_cmd_tx_prep = 0x3, // Transmit preparation
|
||||
cariboulite_radio_state_cmd_tx = 0x4, // Transmit
|
||||
cariboulite_radio_state_cmd_rx = 0x5, // Receive
|
||||
cariboulite_radio_state_transition = 0x6, // State transition in progress
|
||||
cariboulite_radio_state_cmd_reset = 0x7, // Transceiver is in state RESET or SLEEP
|
||||
// if commanded reset, the transceiver state will
|
||||
// automatically end up in state TRXOFF
|
||||
} cariboulite_radio_state_cmd_en;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
cariboulite_radio_rx_bw_200KHz = 0x0,
|
||||
cariboulite_radio_rx_bw_250KHz = 0x1,
|
||||
cariboulite_radio_rx_bw_312KHz = 0x2,
|
||||
cariboulite_radio_rx_bw_400KHz = 0x3,
|
||||
cariboulite_radio_rx_bw_500KHz = 0x4,
|
||||
cariboulite_radio_rx_bw_625KHz = 0x5,
|
||||
cariboulite_radio_rx_bw_787KHz = 0x6,
|
||||
cariboulite_radio_rx_bw_1000KHz = 0x7,
|
||||
cariboulite_radio_rx_bw_1250KHz = 0x8,
|
||||
cariboulite_radio_rx_bw_1562KHz = 0x9,
|
||||
cariboulite_radio_rx_bw_2000KHz = 0xA,
|
||||
cariboulite_radio_rx_bw_2500KHz = 0xB,
|
||||
} cariboulite_radio_rx_bw_en;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
cariboulite_radio_rx_f_cut_0_25_half_fs = 0, // whan 4MSPS => 500 KHz
|
||||
cariboulite_radio_rx_f_cut_0_375_half_fs = 1, // whan 4MSPS => 750 KHz
|
||||
cariboulite_radio_rx_f_cut_0_5_half_fs = 2, // whan 4MSPS => 1000 KHz
|
||||
cariboulite_radio_rx_f_cut_0_75_half_fs = 3, // whan 4MSPS => 1500 KHz
|
||||
cariboulite_radio_rx_f_cut_half_fs = 4, // whan 4MSPS => 2000 KHz
|
||||
} cariboulite_radio_f_cut_en;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
cariboulite_radio_rx_sample_rate_4000khz = 0x1,
|
||||
cariboulite_radio_rx_sample_rate_2000khz = 0x2,
|
||||
cariboulite_radio_rx_sample_rate_1333khz = 0x3,
|
||||
cariboulite_radio_rx_sample_rate_1000khz = 0x4,
|
||||
cariboulite_radio_rx_sample_rate_800khz = 0x5,
|
||||
cariboulite_radio_rx_sample_rate_666khz = 0x6,
|
||||
cariboulite_radio_rx_sample_rate_500khz = 0x8,
|
||||
cariboulite_radio_rx_sample_rate_400khz = 0xA,
|
||||
} cariboulite_radio_sample_rate_en;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
cariboulite_radio_tx_cut_off_80khz = 0x0,
|
||||
cariboulite_radio_tx_cut_off_100khz = 0x1,
|
||||
cariboulite_radio_tx_cut_off_125khz = 0x2,
|
||||
cariboulite_radio_tx_cut_off_160khz = 0x3,
|
||||
cariboulite_radio_tx_cut_off_200khz = 0x4,
|
||||
cariboulite_radio_tx_cut_off_250khz = 0x5,
|
||||
cariboulite_radio_tx_cut_off_315khz = 0x6,
|
||||
cariboulite_radio_tx_cut_off_400khz = 0x7,
|
||||
cariboulite_radio_tx_cut_off_500khz = 0x8,
|
||||
cariboulite_radio_tx_cut_off_625khz = 0x9,
|
||||
cariboulite_radio_tx_cut_off_800khz = 0xA,
|
||||
cariboulite_radio_tx_cut_off_1000khz = 0xB,
|
||||
} cariboulite_radio_tx_cut_off_en;
|
||||
|
||||
typedef struct __attribute__((__packed__))
|
||||
{
|
||||
uint8_t wake_up_por:1;
|
||||
uint8_t trx_ready:1;
|
||||
uint8_t energy_detection_complete:1;
|
||||
uint8_t battery_low:1;
|
||||
uint8_t trx_error:1;
|
||||
uint8_t IQ_if_sync_fail:1;
|
||||
uint8_t res :2;
|
||||
} cariboulite_radio_irq_st;
|
||||
|
||||
typedef struct __attribute__((__packed__))
|
||||
{
|
||||
int16_t i; // LSB
|
||||
int16_t q; // MSB
|
||||
} cariboulite_sample_complex_int16;
|
||||
|
||||
typedef struct __attribute__((__packed__))
|
||||
{
|
||||
uint8_t sync;
|
||||
} cariboulite_sample_meta;
|
||||
|
||||
|
||||
// Frequency Ranges
|
||||
#define CARIBOULITE_6G_MIN (1.0e6)
|
||||
#define CARIBOULITE_6G_MAX (6000.0e6)
|
||||
#define CARIBOULITE_MIN_LO (85.0e6)
|
||||
#define CARIBOULITE_MAX_LO (4200.0e6)
|
||||
#define CARIBOULITE_2G4_MIN (2385.0e6)
|
||||
#define CARIBOULITE_2G4_MAX (2495.0e6)
|
||||
#define CARIBOULITE_S1G_MIN1 (377.0e6)
|
||||
#define CARIBOULITE_S1G_MAX1 (530.0e6)
|
||||
#define CARIBOULITE_S1G_MIN2 (779.0e6)
|
||||
#define CARIBOULITE_S1G_MAX2 (1020.0e6)
|
||||
|
||||
typedef enum
|
||||
{
|
||||
conversion_dir_none = 0,
|
||||
conversion_dir_up = 1,
|
||||
conversion_dir_down = 2,
|
||||
} cariboulite_conversion_dir_en;
|
||||
|
||||
// Radio Struct
|
||||
typedef struct
|
||||
{
|
||||
struct sys_st_t* sys;
|
||||
cariboulite_channel_dir_en channel_direction;
|
||||
cariboulite_channel_en type;
|
||||
bool active;
|
||||
bool cw_output;
|
||||
bool lo_output;
|
||||
|
||||
// MODEM STATES
|
||||
cariboulite_radio_state_cmd_en state;
|
||||
cariboulite_radio_irq_st interrupts;
|
||||
|
||||
bool rx_agc_on;
|
||||
int rx_gain_value_db;
|
||||
|
||||
cariboulite_radio_rx_bw_en rx_bw;
|
||||
cariboulite_radio_f_cut_en rx_fcut;
|
||||
cariboulite_radio_sample_rate_en rx_fs;
|
||||
|
||||
int tx_power;
|
||||
cariboulite_radio_tx_cut_off_en tx_bw;
|
||||
cariboulite_radio_f_cut_en tx_fcut;
|
||||
cariboulite_radio_sample_rate_en tx_fs;
|
||||
|
||||
bool tx_loopback_anabled;
|
||||
|
||||
// at86rf215_radio_energy_detection_st rx_energy_detection;
|
||||
float rx_energy_detection_value;
|
||||
float rx_rssi;
|
||||
|
||||
// FREQUENCY
|
||||
bool modem_pll_locked;
|
||||
bool lo_pll_locked;
|
||||
double lo_frequency;
|
||||
double if_frequency;
|
||||
double actual_rf_frequency;
|
||||
double requested_rf_frequency;
|
||||
double rf_frequency_error;
|
||||
|
||||
// SMI STREAMS
|
||||
int smi_channel_id;
|
||||
|
||||
// OTHERS
|
||||
uint8_t random_value;
|
||||
float rx_thermal_noise_floor;
|
||||
} cariboulite_radio_state_st;
|
||||
|
||||
/**
|
||||
* @brief Initialize a radio device
|
||||
*
|
||||
* Initialize a radio device allocated and given by the user. The radio device
|
||||
* is setup to a known state and the internal variables (in the struct) are initialized.
|
||||
* Note: This function shouldn't normally be used by the end-user as the radio module
|
||||
* is initialized by the "driver initializations sequence" in "cariboulite_setup.h"
|
||||
* and is ready to be used as the driver loading finishes
|
||||
*
|
||||
* @param radio a pre-allocated radio state structure to initialize
|
||||
* @param sys a pointer to the system environment containing the relevant
|
||||
* low level drivers and init / close logic.
|
||||
* @param type the type of the channel (6g, or s1g)
|
||||
* @return 0 = success, -1 = failure
|
||||
*/
|
||||
int cariboulite_radio_init(cariboulite_radio_state_st* radio, struct sys_st_t *sys, cariboulite_channel_en type);
|
||||
/**
|
||||
* @brief Disposing the radio device
|
||||
*
|
||||
* Remove and free the resources taken by the radio device. The system returns to
|
||||
* its known state.
|
||||
* Note: This function shouldn't normally be used by the end-user as the radio module
|
||||
* is disposed by the "driver closing sequence" in "cariboulite_setup.h"
|
||||
* and is supposed to be hidden
|
||||
*
|
||||
* @param radio a pre-allocated radio state structure
|
||||
* @return 0 = success, -1 = failure
|
||||
*/
|
||||
int cariboulite_radio_dispose(cariboulite_radio_state_st* radio);
|
||||
/**
|
||||
* @brief Synchronizing radio struture contents to the hardware actual state
|
||||
*
|
||||
* Reading all the relevant modem internal (hardware) states to update and synchronize
|
||||
* the software state to them.
|
||||
*
|
||||
* @param radio a pre-allocated radio state structure
|
||||
* @return 0 = success, -1 = failure
|
||||
*/
|
||||
int cariboulite_radio_sync_information(cariboulite_radio_state_st* radio);
|
||||
/**
|
||||
* @brief Set modem's external refernece ferquency
|
||||
*
|
||||
* This ext reference is needed for the mixer to lock
|
||||
*
|
||||
* @param sys a pointer to the system environment containing the relevant
|
||||
* low level drivers and init / close logic.
|
||||
* @param ref the used reference source (none, 26MHz, 32MHz, etc.)
|
||||
* @return 0 = success, -1 = failure
|
||||
*/
|
||||
int cariboulite_radio_ext_ref (struct sys_st_t *sys, cariboulite_ext_ref_freq_en ref);
|
||||
|
||||
/**
|
||||
* @brief Get modem chip state
|
||||
*
|
||||
* Getting the modem current state
|
||||
*
|
||||
* @param radio a pre-allocated radio state structure
|
||||
* @param state the modem state (pre-allocated pointer), nullable if not needed
|
||||
* @return 0 = success, -1 = failure
|
||||
*/
|
||||
int cariboulite_radio_get_mod_state (cariboulite_radio_state_st* radio, cariboulite_radio_state_cmd_en *state);
|
||||
|
||||
/**
|
||||
* @brief Get modem irq table
|
||||
*
|
||||
* Reading the IRQ states table of the modem
|
||||
*
|
||||
* @param radio a pre-allocated radio state structure
|
||||
* @param irq_table a table of interrupts state of the modem (nullable if not needed)
|
||||
* @return 0 = success, -1 = failure
|
||||
*/
|
||||
int cariboulite_radio_get_mod_intertupts (cariboulite_radio_state_st* radio, cariboulite_radio_irq_st **irq_table);
|
||||
|
||||
/**
|
||||
* @brief Modem Rx gain control (write)
|
||||
*
|
||||
* Setting the Rx gain properties of the modem
|
||||
*
|
||||
* @param radio a pre-allocated radio state structure
|
||||
* @param rx_agc_on enable modem automatic gain control
|
||||
* @param rx_gain_value_db if AGC is disabled, here we can set the gain manually
|
||||
* @return 0 = success, -1 = failure
|
||||
*/
|
||||
int cariboulite_radio_set_rx_gain_control(cariboulite_radio_state_st* radio,
|
||||
bool rx_agc_on,
|
||||
int rx_gain_value_db);
|
||||
|
||||
/**
|
||||
* @brief Modem Rx gain control (read)
|
||||
*
|
||||
* Getting the Rx gain properties of the modem
|
||||
*
|
||||
* @param radio a pre-allocated radio state structure
|
||||
* @param rx_agc_on modem automatic gain control enabled
|
||||
* @param rx_gain_value_db when AGC is disabled this is the actual gain control
|
||||
* @return 0 = success, -1 = failure
|
||||
*/
|
||||
int cariboulite_radio_get_rx_gain_control(cariboulite_radio_state_st* radio,
|
||||
bool *rx_agc_on,
|
||||
int *rx_gain_value_db);
|
||||
|
||||
/**
|
||||
* @brief Modem Rx gain capabilities
|
||||
*
|
||||
* Reading the modem possible gain limits on Rx
|
||||
*
|
||||
* @param radio a pre-allocated radio state structure
|
||||
* @param rx_min_gain_value_db modem gain limit low [dB]
|
||||
* @param rx_max_gain_value_db modem gain limit high [dB]
|
||||
* @param rx_gain_value_resolution_db modem gain steps [dB]
|
||||
* @return 0 = success, -1 = failure
|
||||
*/
|
||||
int cariboulite_radio_get_rx_gain_limits(cariboulite_radio_state_st* radio,
|
||||
int *rx_min_gain_value_db,
|
||||
int *rx_max_gain_value_db,
|
||||
int *rx_gain_value_resolution_db);
|
||||
|
||||
/**
|
||||
* @brief Modem set RX analog bandwidth
|
||||
*
|
||||
* Setting the RX analog bandwidth
|
||||
*
|
||||
* @param radio a pre-allocated radio state structure
|
||||
* @param rx_bw bandwidth value
|
||||
* @return 0 = success, -1 = failure
|
||||
*/
|
||||
int cariboulite_radio_set_rx_bandwidth(cariboulite_radio_state_st* radio,
|
||||
cariboulite_radio_rx_bw_en rx_bw);
|
||||
int cariboulite_radio_set_rx_bandwidth_flt(cariboulite_radio_state_st* radio, float bw_hz);
|
||||
|
||||
/**
|
||||
* @brief Modem get RX analog bandwidth
|
||||
*
|
||||
* Getting the RX analog bandwidth
|
||||
*
|
||||
* @param radio a pre-allocated radio state structure
|
||||
* @param rx_bw bandwidth value (pointer - pre-allocated), nullable if not needed
|
||||
* @return 0 = success, -1 = failure
|
||||
*/
|
||||
int cariboulite_radio_get_rx_bandwidth(cariboulite_radio_state_st* radio,
|
||||
cariboulite_radio_rx_bw_en *rx_bw);
|
||||
int cariboulite_radio_get_rx_bandwidth_flt(cariboulite_radio_state_st* radio, float* bw_hz);
|
||||
|
||||
/**
|
||||
* @brief Modem set RX sample cut-off bandwidth
|
||||
*
|
||||
* Setting the RX channel sample cut-off bandwidth (digital)
|
||||
*
|
||||
* @param radio a pre-allocated radio state structure
|
||||
* @param rx_sample_rate the used rx sample rate
|
||||
* @param rx_cutoff digital bandwidth
|
||||
* @return 0 = success, -1 = failure
|
||||
*/
|
||||
int cariboulite_radio_set_rx_samp_cutoff(cariboulite_radio_state_st* radio,
|
||||
cariboulite_radio_sample_rate_en rx_sample_rate,
|
||||
cariboulite_radio_f_cut_en rx_cutoff);
|
||||
int cariboulite_radio_set_rx_sample_rate_flt(cariboulite_radio_state_st* radio, float sample_rate_hz);
|
||||
|
||||
/**
|
||||
* @brief Modem get RX sample cut-off bandwidth
|
||||
*
|
||||
* Getting the RX channel sample cut-off bandwidth (digital)
|
||||
*
|
||||
* @param radio a pre-allocated radio state structure
|
||||
* @param rx_sample_rate the used rx sample rate (pointer - pre-allocated), nullable if not needed
|
||||
* @param rx_cutoff digital bandwidth (pointer - pre-allocated), nullable if not needed
|
||||
* @return 0 = success, -1 = failure
|
||||
*/
|
||||
int cariboulite_radio_get_rx_samp_cutoff(cariboulite_radio_state_st* radio,
|
||||
cariboulite_radio_sample_rate_en *rx_sample_rate,
|
||||
cariboulite_radio_f_cut_en *rx_cutoff);
|
||||
int cariboulite_radio_get_rx_sample_rate_flt(cariboulite_radio_state_st* radio, float *sample_rate_hz);
|
||||
|
||||
/**
|
||||
* @brief Modem set TX power
|
||||
*
|
||||
* Setting the Modem's output TX power towards the RFFE
|
||||
*
|
||||
* @param radio a pre-allocated radio state structure
|
||||
* @param tx_power_dbm The TX power in dBm
|
||||
* @return 0 = success, -1 = failure
|
||||
*/
|
||||
|
||||
int cariboulite_radio_set_tx_power(cariboulite_radio_state_st* radio,
|
||||
int tx_power_dbm);
|
||||
|
||||
/**
|
||||
* @brief Modem get TX power
|
||||
*
|
||||
* Getting the Modem's output TX power towards the RFFE
|
||||
*
|
||||
* @param radio a pre-allocated radio state structure
|
||||
* @param tx_power_dbm The TX power in dBm (pointer - pre-allocated), nullable if not needed
|
||||
* @return 0 = success, -1 = failure
|
||||
*/
|
||||
int cariboulite_radio_get_tx_power(cariboulite_radio_state_st* radio,
|
||||
int *tx_power_dbm);
|
||||
|
||||
/**
|
||||
* @brief Modem set Tx analog Bandwidth
|
||||
*
|
||||
* Setting the Modem's Tx analog bandwidth
|
||||
*
|
||||
* @param radio a pre-allocated radio state structure
|
||||
* @param tx_bw bandwidth (according to enumeration)
|
||||
* @return 0 = success, -1 = failure
|
||||
*/
|
||||
|
||||
int cariboulite_radio_set_tx_bandwidth(cariboulite_radio_state_st* radio,
|
||||
cariboulite_radio_tx_cut_off_en tx_bw);
|
||||
int cariboulite_radio_set_tx_bandwidth_flt(cariboulite_radio_state_st* radio, float tx_bw);
|
||||
|
||||
/**
|
||||
* @brief Modem get Tx analog Bandwidth
|
||||
*
|
||||
* Getting the current modem's Tx analog bandwidth
|
||||
*
|
||||
* @param radio a pre-allocated radio state structure
|
||||
* @param tx_bw bandwidth (according to enumeration) - pointer, pre-allocated or nullable if not needed
|
||||
* @return 0 = success, -1 = failure
|
||||
*/
|
||||
int cariboulite_radio_get_tx_bandwidth(cariboulite_radio_state_st* radio,
|
||||
cariboulite_radio_tx_cut_off_en *tx_bw);
|
||||
int cariboulite_radio_get_tx_bandwidth_flt(cariboulite_radio_state_st* radio, float *tx_bw);
|
||||
|
||||
/**
|
||||
* @brief Modem set Tx digital bandwidth
|
||||
*
|
||||
* Setting the modem's Tx digital cut-off bandwidth
|
||||
*
|
||||
* @param radio a pre-allocated radio state structure
|
||||
* @param tx_sample_rate Tx sample rate
|
||||
* @param tx_cutoff digital cut-off bandwidth
|
||||
* @return 0 = success, -1 = failure
|
||||
*/
|
||||
int cariboulite_radio_set_tx_samp_cutoff(cariboulite_radio_state_st* radio,
|
||||
cariboulite_radio_sample_rate_en tx_sample_rate,
|
||||
cariboulite_radio_f_cut_en tx_cutoff);
|
||||
int cariboulite_radio_set_tx_samp_cutoff_flt(cariboulite_radio_state_st* radio, float sample_rate_hz);
|
||||
|
||||
/**
|
||||
* @brief Modem get Tx digital bandwidth
|
||||
*
|
||||
* Getting the current modem's Tx digital cut-off bandwidth
|
||||
*
|
||||
* @param radio a pre-allocated radio state structure
|
||||
* @param tx_sample_rate Tx sample rate, pointer, pre-allocated or nullable if not needed
|
||||
* @param tx_cutoff digital cut-off bandwidth, pointer, pre-allocated or nullable if not needed
|
||||
* @return 0 = success, -1 = failure
|
||||
*/
|
||||
int cariboulite_radio_get_tx_samp_cutoff(cariboulite_radio_state_st* radio,
|
||||
cariboulite_radio_sample_rate_en *tx_sample_rate,
|
||||
cariboulite_radio_f_cut_en *tx_cutoff);
|
||||
int cariboulite_radio_get_tx_samp_cutoff_flt(cariboulite_radio_state_st* radio, float *sample_rate_hz);
|
||||
|
||||
/**
|
||||
* @brief Modem get current RSSI
|
||||
*
|
||||
* Getting the current receive signal strength indication
|
||||
*
|
||||
* @param radio a pre-allocated radio state structure
|
||||
* @param rssi_dbm the RSSI measurement in dBm
|
||||
* @return 0 = success, -1 = failure
|
||||
*/
|
||||
int cariboulite_radio_get_rssi(cariboulite_radio_state_st* radio, float *rssi_dbm);
|
||||
/**
|
||||
* @brief Modem get current Energy Detection Value
|
||||
*
|
||||
* Getting the Rx energy detection value
|
||||
*
|
||||
* @param radio a pre-allocated radio state structure
|
||||
* @param energy_det_val the energy value measurement in dBm
|
||||
* @return 0 = success, -1 = failure
|
||||
*/
|
||||
int cariboulite_radio_get_energy_det(cariboulite_radio_state_st* radio, float *energy_det_val);
|
||||
/**
|
||||
* @brief Modem get a random value
|
||||
*
|
||||
* The modem has an internal true random noise generation capability. This function
|
||||
* returnes the current stored value
|
||||
*
|
||||
* @param radio a pre-allocated radio state structure
|
||||
* @param uint8_t random value, pointer, pre-allocated or nullable if not needed
|
||||
* @return 0 = success, -1 = failure
|
||||
*/
|
||||
int cariboulite_radio_get_rand_val(cariboulite_radio_state_st* radio, uint8_t *rnd);
|
||||
|
||||
/**
|
||||
* @brief Wait for all PLLs to lock
|
||||
*
|
||||
* In the modem this function waits for a safe completion of frequency switch. Until
|
||||
* the modem indicates that the frequency was correctly set, the frequency switch should
|
||||
* not be takes as granted.
|
||||
*
|
||||
* @param radio a pre-allocated radio state structure
|
||||
* @param retries number of retries (typically up ot 5)
|
||||
* @return "0" = didn't lock during the period or "1" - locked successfully
|
||||
*/
|
||||
bool cariboulite_radio_wait_modem_lock(cariboulite_radio_state_st* radio, int retries);
|
||||
bool cariboulite_radio_wait_mixer_lock(cariboulite_radio_state_st* radio, int retries);
|
||||
|
||||
/**
|
||||
* @brief Set modem frequency
|
||||
*
|
||||
* Setting the current frequency of the modem. The resulting frequency
|
||||
* may be slightly different according to the internal PLL granularity
|
||||
* and the resulting frequency is written back to the given pointer.
|
||||
* The user may set the "break before make" to stich off the activation
|
||||
* bit (reset Rx or Tx) and change the frequency in a cold state
|
||||
*
|
||||
* @param radio a pre-allocated radio state structure
|
||||
* @param break_before_make break the current activity and only then set the frequency
|
||||
* @param freq the frequency in Hz, a pointer that carries the frequency to set and
|
||||
* returns back the actual set frequency after the operation is done
|
||||
* @return 0 = success, -1 = failure
|
||||
*/
|
||||
int cariboulite_radio_set_frequency(cariboulite_radio_state_st* radio,
|
||||
bool break_before_make,
|
||||
double *freq);
|
||||
|
||||
/**
|
||||
* @brief Get current actual frequency
|
||||
*
|
||||
* Returns the current modem frequecny (which can be slightly different
|
||||
* for the one set by the "set frequency" function)
|
||||
*
|
||||
* @param radio a pre-allocated radio state structure
|
||||
* @param freq the frequnecy in Hz, pointer, pre-allocated or nullable if not needed
|
||||
* @param lo in case of the high band channel, the LO of the mixer (otherwise unused)
|
||||
* @param i_f in case of the high band channel, this is the IF frequency (otherwise unused)
|
||||
* @return 0 = success, -1 = failure
|
||||
*/
|
||||
int cariboulite_radio_get_frequency(cariboulite_radio_state_st* radio,
|
||||
double *freq, double *lo, double* i_f);
|
||||
|
||||
/**
|
||||
* @brief Activate the channel in a certain state
|
||||
*
|
||||
* Activates the Tx / Rx channel according to the "dir" parameter (either Tx or Rx).
|
||||
* If we want to de-activate the channel, "dir" doesn't matter
|
||||
*
|
||||
* @param radio a pre-allocated radio state structure
|
||||
* @param dir The channel activation direction
|
||||
* @param active either true for activation or false for deactivation
|
||||
* @return 0 = success, -1 = failure
|
||||
*/
|
||||
int cariboulite_radio_activate_channel(cariboulite_radio_state_st* radio,
|
||||
cariboulite_channel_dir_en dir,
|
||||
bool active);
|
||||
|
||||
/**
|
||||
* @brief Set up a CW output upon activation
|
||||
*
|
||||
* When we set the CW output, whenever the channel will be activated in Tx
|
||||
* direction, the modem shall output a CW signal instead of the regular sample
|
||||
* output. The CW frequency will be according to the set frequnecy
|
||||
*
|
||||
* @param radio a pre-allocated radio state structure
|
||||
* @param lo_out setting the LO frequency output directly from the mixer (if 6G)
|
||||
* @param cw_out activate (true) or deactivate (false) CW - the source is the modem
|
||||
* @return 0 = success, -1 = failure
|
||||
*/
|
||||
int cariboulite_radio_set_cw_outputs(cariboulite_radio_state_st* radio,
|
||||
bool lo_out, bool cw_out);
|
||||
|
||||
/**
|
||||
* @brief Get the current CW output state
|
||||
*
|
||||
* Checks whether the CW signal output is on
|
||||
*
|
||||
* @param radio a pre-allocated radio state structure
|
||||
* @param lo_out the LO frequency existence from the mixer
|
||||
* @param cw_out activate (true) or deactivate (false), pointer, pre-allocated,
|
||||
* nullable if not needed
|
||||
* @return 0 = success, -1 = failure
|
||||
*/
|
||||
int cariboulite_radio_get_cw_outputs(cariboulite_radio_state_st* radio,
|
||||
bool *lo_out, bool *cw_out);
|
||||
|
||||
/**
|
||||
* @brief Read samples
|
||||
*
|
||||
* Read the SMI driver and push the samples into the specified buffer of samples.
|
||||
* The number of samples to be read is "length". If needed, a pre-allocated metadata buffer
|
||||
* can be transfered to the function. It will hold the sample-matching metadata such as sync
|
||||
* bits.
|
||||
*
|
||||
* @param radio a pre-allocated radio state structure
|
||||
* @param buffer a pre-allocated buffer of native samples (complex i/q int16)
|
||||
* @param metadata a pre-allocated metadata buffer
|
||||
* @param length the number of I/Q samples to read
|
||||
* @return the number of samples read
|
||||
*/
|
||||
int cariboulite_radio_read_samples(cariboulite_radio_state_st* radio,
|
||||
cariboulite_sample_complex_int16* buffer,
|
||||
cariboulite_sample_meta* metadata,
|
||||
size_t length);
|
||||
|
||||
/**
|
||||
* @brief Write samples
|
||||
*
|
||||
* Write to the SMI driver a certain number of samples.
|
||||
*
|
||||
* @param radio a pre-allocated radio state structure
|
||||
* @param buffer a pre-allocated buffer of native samples (complex i/q int16)
|
||||
* @param length the number of I/Q samples to write
|
||||
* @return the number of samples written
|
||||
*/
|
||||
int cariboulite_radio_write_samples(cariboulite_radio_state_st* radio,
|
||||
cariboulite_sample_complex_int16* buffer,
|
||||
size_t length);
|
||||
|
||||
/**
|
||||
* @brief Get Native Chunk (MTU)
|
||||
*
|
||||
* Gets the SMI IO native chunk size (MTU) in units of samples (4 bytes each)
|
||||
*
|
||||
* @param radio a pre-allocated radio state structure
|
||||
* @return the number of samples in an native sized chunk
|
||||
*/
|
||||
size_t cariboulite_radio_get_native_mtu_size_samples(cariboulite_radio_state_st* radio);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __CARIBOULABS_RADIO_H__
|
|
@ -1,821 +0,0 @@
|
|||
#ifndef ZF_LOG_LEVEL
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#endif
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "CARIBOULITE Setup"
|
||||
#include "zf_log/zf_log.h"
|
||||
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include "cariboulite_setup.h"
|
||||
#include "cariboulite_events.h"
|
||||
#include "cariboulite_fpga_firmware.h"
|
||||
|
||||
|
||||
// Global system object for signals
|
||||
static sys_st* sigsys = NULL;
|
||||
|
||||
//=================================================================
|
||||
static void print_siginfo(siginfo_t *si)
|
||||
{
|
||||
printf("Signal [%d] caught, with the following information: \n", si->si_signo);
|
||||
printf(" signal errno = %d\n", si->si_errno);
|
||||
printf(" signal process pid = %d\n", si->si_pid);
|
||||
printf(" signal process uid = %d\n", (int)si->si_uid);
|
||||
printf(" signal status = %d\n", si->si_status);
|
||||
switch (si->si_code)
|
||||
{
|
||||
case SI_ASYNCIO: printf(" signal errno / async IO completed\n"); break;
|
||||
case SI_KERNEL: printf(" signal errno / signal raised by kernel\n"); break;
|
||||
case SI_MESGQ: printf(" signal errno / state change of POSIX message queue\n"); break;
|
||||
case SI_QUEUE: printf(" signal errno / sigqueue sent a message\n"); break;
|
||||
case SI_TIMER: printf(" signal errno / POSIX timer expiration\n"); break;
|
||||
case SI_TKILL: printf(" signal errno / tkill / tgkill sent the signal\n"); break;
|
||||
case SI_SIGIO: printf(" signal errno / queueing of SIGIO\n"); break;
|
||||
case SI_USER: printf(" signal errno / send by user kill() or raise()\n"); break;
|
||||
default: break;
|
||||
}
|
||||
if (si->si_signo == SIGBUS)
|
||||
switch (si->si_code)
|
||||
{
|
||||
case BUS_ADRALN: printf(" signal errno / SIGNBUS / alignment error\n"); break;
|
||||
case BUS_ADRERR: printf(" signal errno / SIGNBUS / invalid physical address error\n");break;
|
||||
case BUS_OBJERR: printf(" signal errno / SIGNBUS / general hardware arror\n"); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (si->si_signo == SIGCHLD)
|
||||
switch (si->si_code)
|
||||
{
|
||||
case CLD_CONTINUED: printf(" signal errno / SIGCHLD / child stopped but then resumed\n"); break;
|
||||
case CLD_DUMPED: printf(" signal errno / SIGCHLD / child terminated abnormally\n"); break;
|
||||
case CLD_EXITED: printf(" signal errno / SIGCHLD / child terminated normally by exit()\n"); break;
|
||||
case CLD_KILLED: printf(" signal errno / SIGCHLD / child was killed\n"); break;
|
||||
case CLD_STOPPED: printf(" signal errno / SIGCHLD / child stopped\n"); break;
|
||||
case CLD_TRAPPED: printf(" signal errno / SIGCHLD / child hit a trap\n"); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (si->si_signo == SIGFPE)
|
||||
switch (si->si_code)
|
||||
{
|
||||
case FPE_FLTDIV: printf(" signal errno / SIGFPE / division by zero\n"); break;
|
||||
case FPE_FLTOVF: printf(" signal errno / SIGFPE / float operation resulted in an overflow\n"); break;
|
||||
case FPE_FLTINV: printf(" signal errno / SIGFPE / invalid floating operation\n"); break;
|
||||
case FPE_FLTRES: printf(" signal errno / SIGFPE / float operation with invalid or inaxect result\n"); break;
|
||||
case FPE_FLTSUB: printf(" signal errno / SIGFPE / floating operation resulted in an out of range subscript\n"); break;
|
||||
case FPE_FLTUND: printf(" signal errno / SIGFPE / float operation resulted in underflow\n"); break;
|
||||
case FPE_INTDIV: printf(" signal errno / SIGFPE / integer operation - division by zero\n"); break;
|
||||
case FPE_INTOVF: printf(" signal errno / SIGFPE / integet operation resulted in overflow\n"); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (si->si_signo == SIGILL)
|
||||
switch (si->si_code)
|
||||
{
|
||||
case ILL_ILLADR: printf(" signal errno / SIGILL / process attemped to enter an illegal addressing mode\n"); break;
|
||||
case ILL_ILLOPC: printf(" signal errno / SIGILL / process attemped to execute illegal opcode\n"); break;
|
||||
case ILL_ILLOPN: printf(" signal errno / SIGILL / process attemped to execute illegal operand\n"); break;
|
||||
case ILL_PRVOPC: printf(" signal errno / SIGILL / process attemped to execute privileged opcode\n"); break;
|
||||
case ILL_PRVREG: printf(" signal errno / SIGILL / process attemped to execute privileged register\n"); break;
|
||||
case ILL_ILLTRP: printf(" signal errno / SIGILL / process attemped to enter illegal trap\n"); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (si->si_signo == SIGPOLL)
|
||||
switch (si->si_code)
|
||||
{
|
||||
case POLL_ERR: printf(" signal errno / SIGPOLL / poll i/o error occured\n"); break;
|
||||
case POLL_HUP: printf(" signal errno / SIGPOLL / the device hungup othe socked disconnecte\n"); break;
|
||||
case POLL_IN: printf(" signal errno / SIGPOLL / poll - device available for read\n"); break;
|
||||
case POLL_MSG: printf(" signal errno / SIGPOLL / poll - message is available\n"); break;
|
||||
case POLL_OUT: printf(" signal errno / SIGPOLL / poll - device available for writing\n"); break;
|
||||
case POLL_PRI: printf(" signal errno / SIGPOLL / poll - high priority data available for read\n"); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (si->si_signo == SIGSEGV)
|
||||
switch (si->si_code)
|
||||
{
|
||||
case SEGV_ACCERR: printf(" signal errno / SIGSEGV / the process access a valid region of memory in an invalid way - violated memory access permissions\n"); break;
|
||||
case SEGV_MAPERR: printf(" signal errno / SIGSEGV / the process access invalid region of memory\n"); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
/*if (si->si_signo == SIGTRAP)
|
||||
switch (si->si_code)
|
||||
{
|
||||
case TRAP_BRKPT: printf(" signal errno / SIGTRAP / the process hit a breakpoint\n"); break;
|
||||
case TRAP_TRACE: printf(" signal errno / SIGTRAP / the process hit a trace trap\n"); break;
|
||||
default: printf(" signal errno unknown!\n"); break;
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
//=======================================================================================
|
||||
void cariboulite_sigaction_basehandler (int signo,
|
||||
siginfo_t *si,
|
||||
void *ucontext)
|
||||
{
|
||||
int run_first = 0;
|
||||
int run_last = 0;
|
||||
|
||||
// store the errno
|
||||
int internal_errno = errno;
|
||||
|
||||
if (sigsys->signal_cb)
|
||||
{
|
||||
switch(sigsys->sig_op)
|
||||
{
|
||||
case signal_handler_op_last: run_last = 1; break;
|
||||
case signal_handler_op_first: run_first = 1; break;
|
||||
case signal_handler_op_override:
|
||||
default:
|
||||
sigsys->signal_cb(sigsys, sigsys->singal_cb_context, signo, si);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (run_first)
|
||||
{
|
||||
sigsys->signal_cb(sigsys, sigsys->singal_cb_context, signo, si);
|
||||
}
|
||||
|
||||
// The default operation
|
||||
pid_t sender_pid = si->si_pid;
|
||||
printf("CaribouLite: Signal [%d] received from pid=[%d]\n", signo, (int)sender_pid);
|
||||
print_siginfo(si);
|
||||
switch (signo)
|
||||
{
|
||||
case SIGHUP: printf("SIGHUP: Terminal went away!\n"); cariboulite_release_driver(sigsys); break;
|
||||
case SIGINT: printf("SIGINT: interruption\n"); cariboulite_release_driver(sigsys); break;
|
||||
case SIGQUIT: printf("SIGQUIT: user generated quit char\n"); cariboulite_release_driver(sigsys); break;
|
||||
case SIGILL: printf("SIGILL: process tried to execute illegal instruction\n"); cariboulite_release_driver(sigsys); break;
|
||||
case SIGABRT: printf("SIGABRT: sent by abort() command\n"); cariboulite_release_driver(sigsys); break;
|
||||
case SIGBUS: printf("SIGBUS: hardware alignment error\n"); cariboulite_release_driver(sigsys); break;
|
||||
case SIGFPE: printf("SIGFPE: arithmetic exception\n"); cariboulite_release_driver(sigsys); break;
|
||||
case SIGKILL: printf("SIGKILL: upcatchable process termination\n"); cariboulite_release_driver(sigsys); break;
|
||||
case SIGSEGV: printf("SIGSEGV: memory access violation\n"); cariboulite_release_driver(sigsys); break;
|
||||
case SIGTERM: printf("SIGTERM: process termination\n"); cariboulite_release_driver(sigsys); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
//getchar();
|
||||
|
||||
if (run_last)
|
||||
{
|
||||
sigsys->signal_cb(sigsys, sigsys->singal_cb_context, signo, si);
|
||||
}
|
||||
|
||||
errno = internal_errno;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
//=================================================
|
||||
static int cariboulite_setup_signals(sys_st *sys)
|
||||
{
|
||||
cariboulite_setup_signal_handler (sys, NULL, signal_handler_op_last, NULL);
|
||||
int signals[] = {SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGBUS, SIGFPE, SIGSEGV, SIGTERM};
|
||||
struct sigaction sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sigsys = sys;
|
||||
sa.sa_sigaction = cariboulite_sigaction_basehandler;
|
||||
sa.sa_flags |= SA_RESTART | SA_SIGINFO;
|
||||
|
||||
int nsigs = sizeof(signals)/sizeof(signals[0]);
|
||||
|
||||
for (int i = 0; i < nsigs; i++)
|
||||
{
|
||||
if(sigaction(signals[i], &sa, NULL) != 0)
|
||||
{
|
||||
ZF_LOGE("error sigaction() [%d] signal registration", signals[i]);
|
||||
return -cariboulite_signal_registration_failed;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//=======================================================================================
|
||||
int cariboulite_setup_io (sys_st* sys)
|
||||
{
|
||||
ZF_LOGD("Setting up board I/Os");
|
||||
if (io_utils_setup() < 0)
|
||||
{
|
||||
ZF_LOGE("Error setting up io_utils");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (io_utils_spi_init(&sys->spi_dev) < 0)
|
||||
{
|
||||
ZF_LOGE("Error setting up io_utils_spi");
|
||||
io_utils_cleanup();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Setup the initial states for components reset and SS
|
||||
// ICE40
|
||||
io_utils_set_gpio_mode(sys->fpga.cs_pin, io_utils_alt_gpio_out);
|
||||
io_utils_write_gpio(sys->fpga.cs_pin, 1);
|
||||
|
||||
// AT86RF215
|
||||
io_utils_set_gpio_mode(sys->modem.cs_pin, io_utils_alt_gpio_out);
|
||||
io_utils_write_gpio(sys->modem.cs_pin, 1);
|
||||
io_utils_set_gpio_mode(sys->modem.reset_pin, io_utils_alt_gpio_out);
|
||||
io_utils_write_gpio(sys->modem.reset_pin, 0);
|
||||
io_utils_set_gpio_mode(sys->modem.irq_pin, io_utils_alt_gpio_in);
|
||||
|
||||
// RFFC5072
|
||||
io_utils_set_gpio_mode(sys->mixer.cs_pin, io_utils_alt_gpio_out);
|
||||
io_utils_write_gpio(sys->mixer.cs_pin, 1);
|
||||
io_utils_set_gpio_mode(sys->mixer.reset_pin, io_utils_alt_gpio_out);
|
||||
io_utils_write_gpio(sys->mixer.reset_pin, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//=======================================================================================
|
||||
int cariboulite_release_io (sys_st* sys)
|
||||
{
|
||||
ZF_LOGD("Releasing board I/Os - closing SPI");
|
||||
io_utils_spi_close(&sys->spi_dev);
|
||||
|
||||
ZF_LOGD("Releasing board I/Os - io_utils_cleanup");
|
||||
io_utils_cleanup();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//=======================================================================================
|
||||
int cariboulite_configure_fpga (sys_st* sys, cariboulite_firmware_source_en src, char* fpga_bin_path)
|
||||
{
|
||||
int ret = 0;
|
||||
switch (src)
|
||||
{
|
||||
case cariboulite_firmware_source_file:
|
||||
ret = caribou_fpga_program_to_fpga_from_file(&sys->fpga, fpga_bin_path, sys->force_fpga_reprogramming);
|
||||
break;
|
||||
|
||||
case cariboulite_firmware_source_blob:
|
||||
ret = caribou_fpga_program_to_fpga(&sys->fpga, cariboulite_firmware, sizeof(cariboulite_firmware), sys->force_fpga_reprogramming);
|
||||
break;
|
||||
|
||||
default:
|
||||
ZF_LOGE("lattice ice40 configuration source is invalid");
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
caribou_smi_setup_ios(&sys->smi);
|
||||
return ret;
|
||||
}
|
||||
|
||||
//=======================================================================================
|
||||
int cariboulite_init_submodules (sys_st* sys)
|
||||
{
|
||||
int res = 0;
|
||||
ZF_LOGD("initializing submodules");
|
||||
|
||||
// SMI Init
|
||||
//------------------------------------------------------
|
||||
ZF_LOGD("INIT FPGA SMI communication");
|
||||
res = caribou_smi_init(&sys->smi, &sys);
|
||||
if (res < 0)
|
||||
{
|
||||
ZF_LOGE("Error setting up smi submodule");
|
||||
goto cariboulite_init_submodules_fail;
|
||||
}
|
||||
|
||||
// AT86RF215
|
||||
//------------------------------------------------------
|
||||
ZF_LOGD("INIT MODEM - AT86RF215");
|
||||
res = at86rf215_init(&sys->modem, &sys->spi_dev);
|
||||
if (res < 0)
|
||||
{
|
||||
ZF_LOGE("Error initializing modem 'at86rf215'");
|
||||
goto cariboulite_init_submodules_fail;
|
||||
}
|
||||
|
||||
// Configure modem
|
||||
//------------------------------------------------------
|
||||
ZF_LOGD("Configuring modem initial state");
|
||||
at86rf215_setup_rf_irq(&sys->modem, 0, 1, at86rf215_drive_current_2ma);
|
||||
at86rf215_radio_set_state(&sys->modem, at86rf215_rf_channel_900mhz, at86rf215_radio_state_cmd_trx_off);
|
||||
at86rf215_radio_set_state(&sys->modem, at86rf215_rf_channel_2400mhz, at86rf215_radio_state_cmd_trx_off);
|
||||
|
||||
at86rf215_radio_irq_st int_mask = {
|
||||
.wake_up_por = 1,
|
||||
.trx_ready = 1,
|
||||
.energy_detection_complete = 1,
|
||||
.battery_low = 1,
|
||||
.trx_error = 1,
|
||||
.IQ_if_sync_fail = 1,
|
||||
.res = 0,
|
||||
};
|
||||
at86rf215_radio_setup_interrupt_mask(&sys->modem, at86rf215_rf_channel_900mhz, &int_mask);
|
||||
at86rf215_radio_setup_interrupt_mask(&sys->modem, at86rf215_rf_channel_2400mhz, &int_mask);
|
||||
|
||||
at86rf215_iq_interface_config_st modem_iq_config = {
|
||||
.loopback_enable = 0,
|
||||
.drv_strength = at86rf215_iq_drive_current_4ma,
|
||||
.common_mode_voltage = at86rf215_iq_common_mode_v_ieee1596_1v2,
|
||||
.tx_control_with_iq_if = false,
|
||||
.radio09_mode = at86rf215_iq_if_mode,
|
||||
.radio24_mode = at86rf215_iq_if_mode,
|
||||
.clock_skew = at86rf215_iq_clock_data_skew_4_906ns,
|
||||
};
|
||||
at86rf215_setup_iq_if(&sys->modem, &modem_iq_config);
|
||||
|
||||
at86rf215_radio_external_ctrl_st ext_ctrl = {
|
||||
.ext_lna_bypass_available = 0,
|
||||
.agc_backoff = 0,
|
||||
.analog_voltage_external = 0,
|
||||
.analog_voltage_enable_in_off = 1,
|
||||
.int_power_amplifier_voltage = 2,
|
||||
.fe_pad_configuration = 1,
|
||||
};
|
||||
at86rf215_radio_setup_external_settings(&sys->modem, at86rf215_rf_channel_900mhz, &ext_ctrl);
|
||||
at86rf215_radio_setup_external_settings(&sys->modem, at86rf215_rf_channel_2400mhz, &ext_ctrl);
|
||||
|
||||
switch (sys->board_info.numeric_product_id)
|
||||
{
|
||||
//---------------------------------------------------
|
||||
case system_type_cariboulite_full:
|
||||
ZF_LOGD("This board is a Full version CaribouLite - setting ext_ref: modem, 32MHz");
|
||||
// by default the ext_ref for the mixer - from the modem, 32MHz
|
||||
sys->ext_ref_settings.src = cariboulite_ext_ref_src_modem;
|
||||
sys->ext_ref_settings.freq_hz = 32000000;
|
||||
cariboulite_radio_ext_ref (sys, cariboulite_ext_ref_32mhz);
|
||||
break;
|
||||
|
||||
//---------------------------------------------------
|
||||
case system_type_cariboulite_ism:
|
||||
ZF_LOGD("This board is a ISM version CaribouLite - setting ext_ref: OFF");
|
||||
sys->ext_ref_settings.src = cariboulite_ext_ref_src_na;
|
||||
sys->ext_ref_settings.freq_hz = 0;
|
||||
cariboulite_radio_ext_ref (sys, cariboulite_ext_ref_off);
|
||||
break;
|
||||
|
||||
//---------------------------------------------------
|
||||
default:
|
||||
ZF_LOGE("Unknown board type - we sheuldn't get here");
|
||||
break;
|
||||
}
|
||||
|
||||
// The mixer - only relevant to the full version
|
||||
if (sys->board_info.numeric_product_id == system_type_cariboulite_full)
|
||||
{
|
||||
// RFFC5072
|
||||
//------------------------------------------------------
|
||||
ZF_LOGD("INIT MIXER - RFFC5072");
|
||||
res = rffc507x_init(&sys->mixer, &sys->spi_dev);
|
||||
if (res < 0)
|
||||
{
|
||||
ZF_LOGE("Error initializing mixer 'rffc5072'");
|
||||
goto cariboulite_init_submodules_fail;
|
||||
}
|
||||
|
||||
// Configure mixer
|
||||
//------------------------------------------------------
|
||||
//rffc507x_setup_reference_freq(&sys->mixer, 26e6);
|
||||
rffc507x_calibrate(&sys->mixer);
|
||||
}
|
||||
|
||||
// Print the SPI information
|
||||
//io_utils_spi_print_setup(&sys->spi_dev);
|
||||
|
||||
// Initialize the two Radio High-Level devices
|
||||
cariboulite_radio_init(&sys->radio_low, sys, cariboulite_channel_s1g);
|
||||
cariboulite_radio_init(&sys->radio_high, sys, cariboulite_channel_hif);
|
||||
|
||||
cariboulite_radio_set_rx_samp_cutoff(&sys->radio_low, cariboulite_radio_rx_sample_rate_4000khz, cariboulite_radio_rx_f_cut_half_fs);
|
||||
cariboulite_radio_set_tx_samp_cutoff(&sys->radio_low, cariboulite_radio_rx_sample_rate_4000khz, cariboulite_radio_rx_f_cut_half_fs);
|
||||
cariboulite_radio_set_rx_bandwidth(&sys->radio_low, cariboulite_radio_rx_bw_2500KHz);
|
||||
cariboulite_radio_set_tx_bandwidth(&sys->radio_low, cariboulite_radio_tx_cut_off_1000khz);
|
||||
|
||||
cariboulite_radio_set_rx_samp_cutoff(&sys->radio_high, cariboulite_radio_rx_sample_rate_4000khz, cariboulite_radio_rx_f_cut_half_fs);
|
||||
cariboulite_radio_set_tx_samp_cutoff(&sys->radio_high, cariboulite_radio_rx_sample_rate_4000khz, cariboulite_radio_rx_f_cut_half_fs);
|
||||
cariboulite_radio_set_rx_bandwidth(&sys->radio_high, cariboulite_radio_rx_bw_2500KHz);
|
||||
cariboulite_radio_set_tx_bandwidth(&sys->radio_high, cariboulite_radio_tx_cut_off_1000khz);
|
||||
|
||||
cariboulite_radio_activate_channel(&sys->radio_low, cariboulite_channel_dir_rx, false);
|
||||
cariboulite_radio_activate_channel(&sys->radio_high, cariboulite_channel_dir_rx, false);
|
||||
cariboulite_radio_sync_information(&sys->radio_low);
|
||||
cariboulite_radio_sync_information(&sys->radio_high);
|
||||
|
||||
ZF_LOGD("Cariboulite submodules successfully initialized");
|
||||
return 0;
|
||||
|
||||
cariboulite_init_submodules_fail:
|
||||
// release the resources
|
||||
cariboulite_release_submodules(sys);
|
||||
return -1;
|
||||
}
|
||||
|
||||
//=======================================================================================
|
||||
system_type_en cariboulite_get_system_type(sys_st* sys)
|
||||
{
|
||||
return sys->board_info.numeric_product_id;
|
||||
}
|
||||
|
||||
//=======================================================================================
|
||||
int cariboulite_release_submodules(sys_st* sys)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
if (sys->system_status == sys_status_full_init)
|
||||
{
|
||||
// Dispose high-level radio devices
|
||||
cariboulite_radio_dispose(&sys->radio_low);
|
||||
cariboulite_radio_dispose(&sys->radio_high);
|
||||
|
||||
// SMI Module
|
||||
//------------------------------------------------------
|
||||
ZF_LOGD("CLOSE SMI");
|
||||
caribou_smi_close(&sys->smi);
|
||||
usleep(10000);
|
||||
|
||||
// AT86RF215
|
||||
//------------------------------------------------------
|
||||
ZF_LOGD("CLOSE MODEM - AT86RF215");
|
||||
at86rf215_stop_iq_radio_receive (&sys->modem, at86rf215_rf_channel_900mhz);
|
||||
at86rf215_stop_iq_radio_receive (&sys->modem, at86rf215_rf_channel_2400mhz);
|
||||
at86rf215_close(&sys->modem);
|
||||
|
||||
//------------------------------------------------------
|
||||
// RFFC5072 only relevant to the full version
|
||||
if (sys->board_info.numeric_product_id == system_type_cariboulite_full)
|
||||
{
|
||||
ZF_LOGD("CLOSE MIXER - RFFC5072");
|
||||
rffc507x_release(&sys->mixer);
|
||||
}
|
||||
}
|
||||
|
||||
if (sys->system_status == sys_status_minimal_init)
|
||||
{
|
||||
// FPGA Module
|
||||
//------------------------------------------------------
|
||||
ZF_LOGD("CLOSE FPGA communication");
|
||||
res = caribou_fpga_close(&sys->fpga);
|
||||
if (res < 0)
|
||||
{
|
||||
ZF_LOGE("FPGA communication release failed (%d)", res);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//=======================================================================================
|
||||
int cariboulite_self_test(sys_st* sys, cariboulite_self_test_result_st* res)
|
||||
{
|
||||
memset(res, 0, sizeof(cariboulite_self_test_result_st));
|
||||
int error_occured = 0;
|
||||
|
||||
//------------------------------------------------------
|
||||
ZF_LOGD("Testing modem communication and versions");
|
||||
|
||||
uint8_t modem_pn = 0;
|
||||
modem_pn = at86rf215_print_version(&sys->modem);
|
||||
if (modem_pn != 0x34 && modem_pn != 0x35)
|
||||
{
|
||||
ZF_LOGE("The assembled modem is not AT86RF215 / IQ variant (product number: 0x%02x)", modem_pn);
|
||||
res->modem_fail = 1;
|
||||
error_occured = 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------
|
||||
// Mixer only relevant to the full version
|
||||
if (sys->board_info.numeric_product_id == system_type_cariboulite_full)
|
||||
{
|
||||
ZF_LOGD("Testing mixer communication and versions");
|
||||
rffc507x_device_id_st dev_id;
|
||||
rffc507x_readback_status(&sys->mixer, &dev_id, NULL);
|
||||
if (dev_id.device_id != 0x1140 && dev_id.device_id != 0x11C0)
|
||||
{
|
||||
ZF_LOGE("The assembled mixer is not RFFC5071/2[A]");
|
||||
res->mixer_fail = 1;
|
||||
error_occured = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// check and report problems
|
||||
if (!error_occured)
|
||||
{
|
||||
ZF_LOGD("Self-test process finished successfully!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ZF_LOGE("Self-test process finished with errors");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//=================================================
|
||||
int cariboulite_init_system_production(sys_st *sys)
|
||||
{
|
||||
zf_log_set_output_level(ZF_LOG_VERBOSE);
|
||||
ZF_LOGI("driver initializing");
|
||||
|
||||
if (sys->system_status != sys_status_unintialized)
|
||||
{
|
||||
ZF_LOGE("System is already initialized! returning");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// signals
|
||||
ZF_LOGI("Initializing signals");
|
||||
if(cariboulite_setup_signals(sys) != 0)
|
||||
{
|
||||
ZF_LOGE("error signal list registration");
|
||||
return -cariboulite_signal_registration_failed;
|
||||
}
|
||||
|
||||
// IO
|
||||
if (cariboulite_setup_io(sys) != 0)
|
||||
{
|
||||
return -cariboulite_io_setup_failed;
|
||||
}
|
||||
|
||||
// FPGA Init and Programming
|
||||
ZF_LOGD("Initializing FPGA");
|
||||
if (caribou_fpga_init(&sys->fpga, &sys->spi_dev) < 0)
|
||||
{
|
||||
ZF_LOGE("FPGA communication init failed");
|
||||
cariboulite_deinit_system_production(sys);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Initialize the two Radio High-Level devices
|
||||
cariboulite_radio_init(&sys->radio_low, sys, cariboulite_channel_s1g);
|
||||
cariboulite_radio_init(&sys->radio_high, sys, cariboulite_channel_hif);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//=================================================
|
||||
int cariboulite_deinit_system_production(sys_st *sys)
|
||||
{
|
||||
if (sys->sys_type == system_type_cariboulite_full)
|
||||
{
|
||||
ZF_LOGD("CLOSE MIXER - RFFC5072");
|
||||
rffc507x_release(&sys->mixer);
|
||||
}
|
||||
|
||||
caribou_fpga_close(&sys->fpga);
|
||||
|
||||
ZF_LOGI("Releasing board I/Os - closing SPI");
|
||||
io_utils_spi_close(&sys->spi_dev);
|
||||
|
||||
ZF_LOGI("Releasing board I/Os - io_utils_cleanup");
|
||||
io_utils_cleanup();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//=================================================
|
||||
int cariboulite_init_driver_minimal(sys_st *sys, hat_board_info_st *info, bool production)
|
||||
{
|
||||
ZF_LOGD("driver initializing");
|
||||
|
||||
if (sys->system_status != sys_status_unintialized)
|
||||
{
|
||||
ZF_LOGE("System is already initialized! returning");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// LINUX SIGNALS
|
||||
// --------------------------------------------------------------------
|
||||
ZF_LOGD("Initializing signals");
|
||||
if(cariboulite_setup_signals(sys) != 0)
|
||||
{
|
||||
ZF_LOGE("error signal list registration");
|
||||
return -cariboulite_signal_registration_failed;
|
||||
}
|
||||
|
||||
// DETECT BOARD FROM DEVICE-TREE OR EEPROM
|
||||
// --------------------------------------------------------------------
|
||||
if (hat_detect_board(&sys->board_info) == 0)
|
||||
{
|
||||
if (hat_detect_from_eeprom(&sys->board_info) != 1)
|
||||
{
|
||||
ZF_LOGE("Failed to detect the board in /proc/device-tree/hat - EEPROM needs to be configured.");
|
||||
ZF_LOGE("Please run the cariboulite_prod application with sudo permissions...");
|
||||
}
|
||||
else
|
||||
{
|
||||
ZF_LOGE("Failed to detect the board in /proc/device-tree/hat, though EEPROM is configured. Please reboot system...");
|
||||
}
|
||||
return -cariboulite_board_detection_failed;
|
||||
}
|
||||
sys->sys_type = (system_type_en)sys->board_info.numeric_product_id;
|
||||
|
||||
// CONFIGURE I/O
|
||||
// --------------------------------------------------------------------
|
||||
if (cariboulite_setup_io(sys) != 0)
|
||||
{
|
||||
return -cariboulite_io_setup_failed;
|
||||
}
|
||||
|
||||
// FPGA Init and Programming
|
||||
ZF_LOGD("Initializing FPGA");
|
||||
if (caribou_fpga_init(&sys->fpga, &sys->spi_dev) < 0)
|
||||
{
|
||||
ZF_LOGE("FPGA communication init failed");
|
||||
cariboulite_release_io (sys);
|
||||
return -cariboulite_fpga_configuration_failed;
|
||||
}
|
||||
|
||||
ZF_LOGD("Programming FPGA");
|
||||
if (cariboulite_configure_fpga (sys, cariboulite_firmware_source_blob, NULL/*sys->firmware_path_operational*/) < 0)
|
||||
{
|
||||
ZF_LOGE("FPGA programming failed");
|
||||
caribou_fpga_close(&sys->fpga);
|
||||
cariboulite_release_io (sys);
|
||||
return -cariboulite_fpga_configuration_failed;
|
||||
}
|
||||
if (sys->reset_fpga_on_startup)
|
||||
{
|
||||
//caribou_fpga_soft_reset(&sys->fpga);
|
||||
}
|
||||
|
||||
// Reading the configuration from the FPGA (resistor set)
|
||||
int led0 = 0, led1 = 0, btn = 0, cfg = 0;
|
||||
caribou_fpga_get_io_ctrl_dig (&sys->fpga, &led0, &led1, &btn, &cfg);
|
||||
|
||||
ZF_LOGD("FPGA Digital Values: led0: %d, led1: %d, btn: %d, CFG[0..3]: [%d,%d,%d,%d]",
|
||||
led0, led1, btn, (cfg >> 0) & 0x1, (cfg >> 1) & 0x1, (cfg >> 2) & 0x1, (cfg >> 3) & 0x1);
|
||||
sys->fpga_config_resistor_state = cfg;
|
||||
|
||||
// if we are in the production phase, don't check hat configurations
|
||||
if (production)
|
||||
{
|
||||
sys->system_status = sys_status_minimal_init;
|
||||
return cariboulite_ok;
|
||||
}
|
||||
|
||||
ZF_LOGD("Detected Board Information:");
|
||||
cariboulite_print_board_info(sys, true);
|
||||
|
||||
// if the board info is requested by caller, copy it
|
||||
if (info) memcpy(info, &sys->board_info, sizeof(hat_board_info_st));
|
||||
|
||||
|
||||
sys->system_status = sys_status_minimal_init;
|
||||
|
||||
return cariboulite_ok;
|
||||
}
|
||||
|
||||
//=================================================
|
||||
void cariboulite_set_log_level(cariboulite_log_level_en lvl)
|
||||
{
|
||||
if (lvl == cariboulite_log_level_verbose)
|
||||
{
|
||||
zf_log_set_output_level(ZF_LOG_VERBOSE);
|
||||
}
|
||||
else if (lvl == cariboulite_log_level_info)
|
||||
{
|
||||
zf_log_set_output_level(ZF_LOG_INFO);
|
||||
}
|
||||
else if (lvl == cariboulite_log_level_none)
|
||||
{
|
||||
zf_log_set_output_level(ZF_LOG_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
//=================================================
|
||||
int cariboulite_init_driver(sys_st *sys, hat_board_info_st *info)
|
||||
{
|
||||
int ret = cariboulite_init_driver_minimal(sys, info, false);
|
||||
if (ret < 0)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (sys->system_status == sys_status_full_init)
|
||||
{
|
||||
ZF_LOGE("System is already fully initialized!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// INIT other sub-modules
|
||||
if (cariboulite_init_submodules (sys) != 0)
|
||||
{
|
||||
caribou_fpga_close(&sys->fpga);
|
||||
cariboulite_release_io (sys);
|
||||
return -cariboulite_submodules_init_failed;
|
||||
}
|
||||
|
||||
// Self-Test
|
||||
cariboulite_self_test_result_st self_tes_res = {0};
|
||||
if (cariboulite_self_test(sys, &self_tes_res) != 0)
|
||||
{
|
||||
caribou_fpga_close(&sys->fpga);
|
||||
cariboulite_release_io (sys);
|
||||
return -cariboulite_self_test_failed;
|
||||
}
|
||||
|
||||
sys->system_status = sys_status_full_init;
|
||||
|
||||
return cariboulite_ok;
|
||||
}
|
||||
|
||||
//=================================================
|
||||
int cariboulite_setup_signal_handler (sys_st *sys,
|
||||
signal_handler handler,
|
||||
signal_handler_operation_en op,
|
||||
void *context)
|
||||
{
|
||||
ZF_LOGD("setting up signal handler");
|
||||
|
||||
sys->signal_cb = handler;
|
||||
sys->singal_cb_context = context;
|
||||
sys->sig_op = op;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//=================================================
|
||||
void cariboulite_release_driver(sys_st *sys)
|
||||
{
|
||||
ZF_LOGD("driver being released");
|
||||
if (sys->system_status != sys_status_unintialized)
|
||||
{
|
||||
//caribou_fpga_set_io_ctrl_mode (&sys->fpga, false, ...);
|
||||
cariboulite_release_submodules(sys);
|
||||
cariboulite_release_io (sys);
|
||||
|
||||
sys->system_status = sys_status_unintialized;
|
||||
}
|
||||
ZF_LOGD("driver released");
|
||||
}
|
||||
|
||||
//=================================================
|
||||
int cariboulite_get_serial_number(sys_st *sys, uint32_t* serial_number, int *count)
|
||||
{
|
||||
if (serial_number) *serial_number = sys->board_info.numeric_serial_number;
|
||||
if (count) *count = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//=================================================
|
||||
void cariboulite_lib_version(cariboulite_lib_version_st* v)
|
||||
{
|
||||
v->major_version = CARIBOULITE_MAJOR_VERSION;
|
||||
v->minor_version = CARIBOULITE_MINOR_VERSION;
|
||||
v->revision = CARIBOULITE_REVISION;
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
int cariboulite_detect_board(sys_st *sys)
|
||||
{
|
||||
if (hat_detect_board(&sys->board_info) == 0)
|
||||
{
|
||||
// the board was not configured as a hat. Lets try and detect it directly
|
||||
// through its EEPROM
|
||||
if (hat_detect_from_eeprom(&sys->board_info) == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
sys->sys_type = (system_type_en)sys->board_info.numeric_product_id;
|
||||
return 1;
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
void cariboulite_print_board_info(sys_st *sys, bool log)
|
||||
{
|
||||
hat_print_board_info(&sys->board_info, log);
|
||||
|
||||
if (log)
|
||||
{
|
||||
switch (sys->sys_type)
|
||||
{
|
||||
case system_type_cariboulite_full: ZF_LOGD("# Board Info - Product Type: CaribouLite FULL"); break;
|
||||
case system_type_cariboulite_ism: ZF_LOGD("# Board Info - Product Type: CaribouLite ISM"); break;
|
||||
case system_type_unknown:
|
||||
default: ZF_LOGD("# Board Info - Product Type: Unknown"); break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (sys->sys_type)
|
||||
{
|
||||
case system_type_cariboulite_full: printf(" Product Type: CaribouLite FULL"); break;
|
||||
case system_type_cariboulite_ism: printf(" Product Type: CaribouLite ISM"); break;
|
||||
case system_type_unknown:
|
||||
default: printf(" Product Type: Unknown"); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
cariboulite_radio_state_st* cariboulite_get_radio_handle(sys_st* sys, cariboulite_channel_en type)
|
||||
{
|
||||
if (type == cariboulite_channel_s1g) return &sys->radio_low;
|
||||
else if (type == cariboulite_channel_hif) return &sys->radio_high;
|
||||
return NULL;
|
||||
}
|
|
@ -1,280 +0,0 @@
|
|||
/**
|
||||
* @file cariboulite_setup.h
|
||||
* @author David Michaeli
|
||||
* @date March 2023
|
||||
* @brief System level setup functionality
|
||||
*
|
||||
* High level fucntionality - initialization, de-initialization,
|
||||
* drivers loading, FPGA programming etc.
|
||||
*/
|
||||
|
||||
#ifndef __CARIBOULITE_SETUP_H__
|
||||
#define __CARIBOULITE_SETUP_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "cariboulite.h"
|
||||
#include "cariboulite_internal.h"
|
||||
#include "cariboulite_radio.h"
|
||||
#include "caribou_fpga/caribou_fpga.h"
|
||||
#include "at86rf215/at86rf215.h"
|
||||
#include "rffc507x/rffc507x.h"
|
||||
#include "caribou_smi/caribou_smi.h"
|
||||
#include "io_utils/io_utils.h"
|
||||
#include "io_utils/io_utils_spi.h"
|
||||
#include "io_utils/io_utils_sys_info.h"
|
||||
#include "cariboulite_config_default.h"
|
||||
|
||||
#define CARIBOULITE_MAJOR_VERSION 1
|
||||
#define CARIBOULITE_MINOR_VERSION 2
|
||||
#define CARIBOULITE_REVISION 0
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int fpga_fail;
|
||||
int modem_fail;
|
||||
int mixer_fail;
|
||||
int smi_fail;
|
||||
} cariboulite_self_test_result_st;
|
||||
|
||||
/**
|
||||
* @brief FPGA programming source definition
|
||||
*
|
||||
* While programming the FPGA, using "cariboulite_configure_fpga", it
|
||||
* expects to receive the FGPA binary file to program either as a
|
||||
* binary file full path or by a memory allocated buffer blob.
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
cariboulite_firmware_source_file = 0,
|
||||
cariboulite_firmware_source_blob = 1,
|
||||
} cariboulite_firmware_source_en;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
cariboulite_ok = 0,
|
||||
cariboulite_board_detection_failed = 1,
|
||||
cariboulite_io_setup_failed = 2,
|
||||
cariboulite_fpga_configuration_failed = 3,
|
||||
cariboulite_submodules_init_failed = 4,
|
||||
cariboulite_self_test_failed = 5,
|
||||
cariboulite_board_dependent_config_failed = 6,
|
||||
cariboulite_signal_registration_failed = 7,
|
||||
} cariboulite_errors_en;
|
||||
|
||||
/**
|
||||
* @brief Detects the connected board and returns the detection result
|
||||
*
|
||||
* When a fully configures board is mounted, it is being pre-detected by the
|
||||
* Linux HAT configuration during startup. Then this information is written as
|
||||
* a "device-tree"-hat sysfs configuration. This configuration can be found in
|
||||
* '/proc/device-tree/hat/...' files. They populate the system type, version,
|
||||
* type and other attributes. This function tries to read these attributes to determine
|
||||
* the type of system that is connected. If it doesn't find the system, it tries to
|
||||
* further query it directrly through its i2c interface (EEPROM device on board).
|
||||
* If all these doesn't work - it fails.
|
||||
*
|
||||
* @param sys a pre-allocated device handle structure
|
||||
* @return success (1) or failure (0)
|
||||
*/
|
||||
int cariboulite_detect_board(sys_st *sys);
|
||||
|
||||
/**
|
||||
* @brief Printing detected board information
|
||||
*
|
||||
* Once the detection succeeded, this function can print the device
|
||||
* information within the HAT interface.
|
||||
*
|
||||
* @param sys a pre-allocated device handle structure
|
||||
* @param log if true - print out the information on as log entries. Otherwise print
|
||||
* it as a stdout (printf) output.
|
||||
*/
|
||||
void cariboulite_print_board_info(sys_st *sys, bool log);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Setting global logging level
|
||||
*
|
||||
* options: cariboulite_log_level_verbose, cariboulite_log_level_info, cariboulite_log_level_none
|
||||
*/
|
||||
void cariboulite_set_log_level(cariboulite_log_level_en lvl);
|
||||
|
||||
/**
|
||||
* @brief Fully initialize the system
|
||||
*
|
||||
* This function performs fully entry initialization of the system and
|
||||
* a short self-test sequence to communication and check all the components
|
||||
* respond.
|
||||
*
|
||||
* @param sys a pre-allocated device handle structure
|
||||
* @param info the initialization performs internally the board detection sequence.
|
||||
* which is stored into the "sys" struct. If the user wants to receive the
|
||||
* information explicitely, he can pass here a pre-allocated info structure and
|
||||
* it will be filled with the board information. If this is not needed
|
||||
* user can pass "NULL"
|
||||
* @return success / fail codes according to "cariboulite_errors_en"
|
||||
*/
|
||||
int cariboulite_init_driver(sys_st *sys, hat_board_info_st *info);
|
||||
|
||||
/**
|
||||
* @brief Partially initialize the system
|
||||
*
|
||||
* Partial initialization of the system (low level drivers only) without the
|
||||
* SDR functionality. This is mainly used for production where we want only to program the
|
||||
* FPGA and test the system at its low level.
|
||||
*
|
||||
* @param sys a pre-allocated device handle structure
|
||||
* @param info the initialization performs internally the board detection sequence.
|
||||
* which is stored into the "sys" struct. If the user wants to receive the
|
||||
* information explicitely, he can pass here a pre-allocated info structure and
|
||||
* it will be filled with the board information. If this is not needed
|
||||
* user can pass "NULL"
|
||||
* @return success / fail codes according to "cariboulite_errors_en"
|
||||
*/
|
||||
int cariboulite_init_driver_minimal(sys_st *sys, hat_board_info_st *info, bool production);
|
||||
int cariboulite_init_system_production(sys_st *sys);
|
||||
int cariboulite_deinit_system_production(sys_st *sys);
|
||||
|
||||
/**
|
||||
* @brief Register an explicit linux signal handler in the application level
|
||||
*
|
||||
* If a linux signal occures it is caught by the system. The user may want to be
|
||||
* notified on the signal in the app level. This is done by registering a handler
|
||||
* function with this fucntion.
|
||||
*
|
||||
* @param sys a pre-allocated device handle structure
|
||||
* @param handler a handler function with the following prototype:
|
||||
* void (*signal_handler)( struct sys_st_t *sys, // the current cariboulite low-level management struct
|
||||
* void* context, // custom context - can be a higher level app class
|
||||
* int signal_number, // the signal number
|
||||
* siginfo_t *si);
|
||||
* @param op determines when the handler is shot:
|
||||
* signal_handler_op_last => The custom sighandler operates (if present) after the default sig handler
|
||||
* signal_handler_op_first => The custom sighandler operates (if present) before the default sig handler
|
||||
* signal_handler_op_override => The custom sighandler operates (if present) instead of the default sig handler
|
||||
* @param context this void* pointer may contain an app specific handle to be passed to the explicit signal handler.
|
||||
* @return always 0 (success)
|
||||
*/
|
||||
int cariboulite_setup_signal_handler (sys_st *sys,
|
||||
signal_handler handler,
|
||||
signal_handler_operation_en op,
|
||||
void *context);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Program and configure the FPGA
|
||||
*
|
||||
* Programming the FPGA using either an external binary file (.bin) or
|
||||
* an image of the file within the local memory (a buffer).
|
||||
*
|
||||
* @param sys a pre-allocated device handle structure
|
||||
* @param src the source of the binary file. In case of the "blob" choise,
|
||||
* the binary file is stored inside an array named "cariboulite_fpga_firmware"
|
||||
* which is automatically generated after FPGA binary is created. This
|
||||
* buffer is located inside a file named "cariboulite_fpga_firmware.h"
|
||||
* @param fpga_bin_path the path of the binary file - only applicable if
|
||||
* "cariboulite_firmware_source_file" was chosen as the "src". otherwise
|
||||
* NULL should be passed here.
|
||||
* @return 0 (success), -1 (fail)
|
||||
*/
|
||||
int cariboulite_configure_fpga (sys_st* sys, cariboulite_firmware_source_en src, char* fpga_bin_path);
|
||||
|
||||
/**
|
||||
* @brief Release resources
|
||||
*
|
||||
* Releasing resources taken during init and program execution. Should be
|
||||
* called at the end of the session.
|
||||
*
|
||||
* @param sys a pre-allocated device handle structure
|
||||
*/
|
||||
void cariboulite_release_driver(sys_st *sys);
|
||||
|
||||
/**
|
||||
* @brief Get current API lib version
|
||||
*
|
||||
* @param v a struct containing the version information
|
||||
*/
|
||||
void cariboulite_lib_version(cariboulite_lib_version_st* v);
|
||||
|
||||
/**
|
||||
* @brief Get board serial number
|
||||
*
|
||||
* Note - this 32bit serial number is a digest value of the UUID 128 bit that
|
||||
* is automatically generated for each board. It is basically the result of
|
||||
* "xor" operations on the UUID's parts.
|
||||
*
|
||||
* @param sys a pre-allocated device handle structure
|
||||
* @param serial_number a 32bit storage to store the serial number (can be NULL - if not needed)
|
||||
* @param count the number of 32bit variables returned - always returns as "1".
|
||||
* @return always 0
|
||||
*/
|
||||
int cariboulite_get_serial_number(sys_st *sys, uint32_t* serial_number, int *count);
|
||||
|
||||
/**
|
||||
* @brief Initilize IOs
|
||||
*
|
||||
* This is a sub-part of the initialization sequence and it
|
||||
* initializes only the IOs (GPIOs, interfaces) of the RPI
|
||||
*
|
||||
* @param sys a pre-allocated device handle structure
|
||||
* @return 0 (sucess), -1 (fail)
|
||||
*/
|
||||
int cariboulite_setup_io (sys_st* sys);
|
||||
|
||||
/**
|
||||
* @brief Release IOs
|
||||
*
|
||||
* Giving back the IOs and returning them to their default state
|
||||
*
|
||||
* @param sys a pre-allocated device handle structure
|
||||
* @return 0 (sucess), -1 (fail)
|
||||
*/
|
||||
int cariboulite_release_io (sys_st* sys);
|
||||
int cariboulite_init_submodules (sys_st* sys);
|
||||
int cariboulite_release_submodules(sys_st* sys);
|
||||
|
||||
/**
|
||||
* @brief Component communication test
|
||||
*
|
||||
* Self testing communication with components, their versions, etc.
|
||||
*
|
||||
* @param sys a pre-allocated device handle structure
|
||||
* @param res test-result
|
||||
* @return 0 (sucess), -1 (fail)
|
||||
*/
|
||||
int cariboulite_self_test(sys_st* sys, cariboulite_self_test_result_st* res);
|
||||
|
||||
/**
|
||||
* @brief Getting the used radio handle
|
||||
*
|
||||
* After initializing the drivers, a radio device is created and stored in
|
||||
* the device driver main struct. To manipulate the radio features, this radio
|
||||
* handle (pointer) needs to be obtained by the user. This handle is normally
|
||||
* passed to the radio manipulation functions in "cariboulite_radio.h"
|
||||
*
|
||||
* @param sys a pre-allocated device handle structure
|
||||
* @param res test-result
|
||||
* @return 0 (sucess), -1 (fail)
|
||||
*/
|
||||
cariboulite_radio_state_st* cariboulite_get_radio_handle(sys_st* sys, cariboulite_channel_en type);
|
||||
|
||||
/**
|
||||
* @brief Getting CaribouLite Type
|
||||
*
|
||||
* Returns the version of the hardware - ISM / 6G / Unknown
|
||||
* ISM has two channels - 900MHz and 2.4 GHz
|
||||
* 6G has two channels - 900MHz and 6000MHz
|
||||
*
|
||||
* @return according to cariboulite_version_en
|
||||
*/
|
||||
system_type_en cariboulite_get_system_type(sys_st* sys);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif // __CARIBOULITE_SETUP_H__
|
|
@ -1,95 +0,0 @@
|
|||
#ifndef ZF_LOG_LEVEL
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#endif
|
||||
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "CARIBOULITE Test"
|
||||
#include "zf_log/zf_log.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "cariboulite_setup.h"
|
||||
#include "cariboulite_events.h"
|
||||
#include "cariboulite.h"
|
||||
#include "hat/hat.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct sigaction act;
|
||||
int program_running = 1;
|
||||
int signal_shown = 0;
|
||||
CARIBOULITE_CONFIG_DEFAULT(cariboulite_sys);
|
||||
|
||||
int app_menu(sys_st* sys);
|
||||
|
||||
//=================================================
|
||||
int stop_program ()
|
||||
{
|
||||
if (program_running) ZF_LOGD("program termination requested");
|
||||
program_running = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//=================================================
|
||||
void sighandler( struct sys_st_t *sys,
|
||||
void* context,
|
||||
int signal_number,
|
||||
siginfo_t *si)
|
||||
{
|
||||
if (signal_shown != signal_number)
|
||||
{
|
||||
ZF_LOGI("Received signal %d", signal_number);
|
||||
signal_shown = signal_number;
|
||||
}
|
||||
|
||||
switch (signal_number)
|
||||
{
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
case SIGABRT:
|
||||
case SIGILL:
|
||||
case SIGSEGV:
|
||||
case SIGFPE: stop_program(); break;
|
||||
default: return; break;
|
||||
}
|
||||
}
|
||||
|
||||
//=================================================
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// init the program
|
||||
cariboulite_sys.force_fpga_reprogramming = 0;
|
||||
if (cariboulite_init_driver(&cariboulite_sys, NULL)!=0)
|
||||
{
|
||||
ZF_LOGE("driver init failed, terminating...");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// setup the signal handler
|
||||
cariboulite_setup_signal_handler (&cariboulite_sys, sighandler, signal_handler_op_last, &cariboulite_sys);
|
||||
|
||||
sleep(1);
|
||||
while (program_running)
|
||||
{
|
||||
int ret = app_menu(&cariboulite_sys);
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
ZF_LOGE("Error occurred, terminating...");
|
||||
break;
|
||||
}
|
||||
else if (ret == 0)
|
||||
{
|
||||
ZF_LOGI("Quit command => terminating...");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// close the driver and release resources
|
||||
cariboulite_release_driver(&cariboulite_sys);
|
||||
return 0;
|
||||
}
|
|
@ -1,366 +0,0 @@
|
|||
#ifndef ZF_LOG_LEVEL
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#endif
|
||||
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "CARIBOULITE Main"
|
||||
#include "zf_log/zf_log.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "cariboulite_setup.h"
|
||||
#include "cariboulite_events.h"
|
||||
#include "cariboulite.h"
|
||||
#include "hat/hat.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
//=======================================================================
|
||||
// INTERNAL VARIABLES AND DEFINITIONS
|
||||
|
||||
static struct sigaction act;
|
||||
static int signal_shown = 0;
|
||||
CARIBOULITE_CONFIG_STATIC_DEFAULT(cariboulite_sys);
|
||||
|
||||
// Program state structure
|
||||
typedef struct
|
||||
{
|
||||
// Arguments
|
||||
char *filename;
|
||||
int rx_channel;
|
||||
double frequency;
|
||||
float gain;
|
||||
float ppm_error;
|
||||
size_t samples_to_read;
|
||||
int force_fpga_prog;
|
||||
int write_metadata;
|
||||
|
||||
// State
|
||||
int sample_infinite;
|
||||
int program_running;
|
||||
int sys_type;
|
||||
size_t native_read_len;
|
||||
cariboulite_sample_complex_int16* buffer;
|
||||
cariboulite_sample_meta* metadata;
|
||||
cariboulite_radio_state_st *radio;
|
||||
FILE *file;
|
||||
} prog_state_st;
|
||||
|
||||
static prog_state_st state = {0};
|
||||
|
||||
//=================================================
|
||||
static int stop_program (void)
|
||||
{
|
||||
if (state.program_running) ZF_LOGD("program termination requested");
|
||||
state.program_running = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//=================================================
|
||||
static void sighandler(void* context,
|
||||
int signal_number,
|
||||
siginfo_t *si)
|
||||
{
|
||||
struct sys_st_t *s = (struct sys_st_t *)context;
|
||||
|
||||
if (signal_shown != signal_number)
|
||||
{
|
||||
ZF_LOGI("Received signal %d", signal_number);
|
||||
signal_shown = signal_number;
|
||||
}
|
||||
|
||||
switch (signal_number)
|
||||
{
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
case SIGABRT:
|
||||
case SIGILL:
|
||||
case SIGSEGV:
|
||||
case SIGFPE: stop_program(); break;
|
||||
default: return; break;
|
||||
}
|
||||
}
|
||||
|
||||
//=================================================
|
||||
static void init_program_state(void)
|
||||
{
|
||||
state.filename = NULL;
|
||||
state.rx_channel = 0; // low freq channel
|
||||
state.frequency = 915e6;
|
||||
state.gain = 0;
|
||||
state.ppm_error = 0;
|
||||
state.samples_to_read = 1024*1024/8;
|
||||
state.force_fpga_prog = 0;
|
||||
state.write_metadata = 0;
|
||||
|
||||
// state
|
||||
state.sample_infinite = 1;
|
||||
state.program_running = 1;
|
||||
state.sys_type = system_type_cariboulite_ism;
|
||||
state.native_read_len = 1024 * 1024 / 8;
|
||||
state.buffer = NULL;
|
||||
state.metadata = NULL;
|
||||
state.radio = NULL;
|
||||
state.file = NULL;
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
static void usage(void)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"CaribouLite I/Q recorder\n\n"
|
||||
"Usage:\t-c the RX channel to use (0: low, 1: high)\n"
|
||||
"\t-f frequency [Hz]\n"
|
||||
"\t[-g gain (default: -1 for agc)]\n"
|
||||
"\t[-p ppm_error (default: 0)]\n"
|
||||
"\t[-n number of samples to read (default: 0, infinite)]\n"
|
||||
"\t[-S force sync output (default: async)]\n"
|
||||
"\t[-F force fpga reprogramming (default: '0')]\n"
|
||||
"\t[-M write metadata (default: '0')]\n"
|
||||
"\tfilename ('-' dumps samples to stdout)\n\n"
|
||||
"Example:\n"
|
||||
"\t1. Sample S1G channel at 905MHz into filename capture.bin\n"
|
||||
"\t\tcariboulite_util -c 0 -f 905000000 capture.bin\n"
|
||||
"\t2. Sample S1G channel at 905MHz into filename capture.bin, only 30000 samples\n"
|
||||
"\t\tcariboulite_util -c 0 -f 905000000 -n 30000 capture.bin\n\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
static int check_inputs(void)
|
||||
{
|
||||
state.sys_type = cariboulite_sys.board_info.numeric_product_id;
|
||||
|
||||
if (state.rx_channel != 0 && state.rx_channel != 1)
|
||||
{
|
||||
ZF_LOGE("Radio selection incompatible [%d] (should be either '0' or '1')", state.rx_channel);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (state.rx_channel == 0 &&
|
||||
(state.frequency < CARIBOULITE_S1G_MIN1 || state.frequency > CARIBOULITE_S1G_MAX2 ||
|
||||
(state.frequency > CARIBOULITE_S1G_MAX1 && state.frequency < CARIBOULITE_S1G_MIN2)) )
|
||||
{
|
||||
ZF_LOGE("S1G radio frequency (%.2f) is out of the [%.0f .. %.0f, %.0f .. %.0f] MHz range", state.frequency,
|
||||
CARIBOULITE_S1G_MIN1/1e6, CARIBOULITE_S1G_MAX1/1e6, CARIBOULITE_S1G_MIN2/1e6, CARIBOULITE_S1G_MAX2/1e6);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (state.rx_channel == 1 && state.sys_type == system_type_cariboulite_full &&
|
||||
(state.frequency < CARIBOULITE_6G_MIN && state.frequency > CARIBOULITE_6G_MAX))
|
||||
{
|
||||
ZF_LOGE("HiF (full) radio frequency (%.2f) is out of the [%.0f .. %.0f] MHz range", state.frequency,
|
||||
CARIBOULITE_6G_MIN/1e6, CARIBOULITE_6G_MAX/1e6);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (state.rx_channel == 1 && state.sys_type == system_type_cariboulite_ism &&
|
||||
(state.frequency < CARIBOULITE_2G4_MIN && state.frequency > CARIBOULITE_2G4_MAX))
|
||||
{
|
||||
ZF_LOGE("HiF (ISM) radio frequency (%.2f) is out of the [%.0f .. %.0f] MHz range", state.frequency,
|
||||
CARIBOULITE_2G4_MIN/1e6, CARIBOULITE_2G4_MAX/1e6);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((state.gain < 0 || state.gain > 23.0*3.0) && state.gain != -1)
|
||||
{
|
||||
ZF_LOGE("Rx channel gain %.0f is incompatible (legal range: [%.0f .. %.0f] dB", state.gain,
|
||||
0.0, 23.0*3.0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//=================================================
|
||||
int analyze_arguments(int argc, char *argv[])
|
||||
{
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "c:f:g:n:S:F")) != -1) {
|
||||
switch (opt) {
|
||||
case 'c':
|
||||
state.rx_channel = (int)atoi(optarg);
|
||||
printf("DBG: RX Channel = %d\n", state.rx_channel);
|
||||
break;
|
||||
case 'f':
|
||||
state.frequency = atof(optarg);
|
||||
printf("DBG: Frequency = %.1f Hz\n", state.frequency);
|
||||
break;
|
||||
case 'g':
|
||||
state.gain = (int)(atof(optarg));
|
||||
if (state.gain == -1)
|
||||
{
|
||||
printf("DBG: Rx Gain = AGC\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("DBG: Rx Gain = %.1f dB\n", state.gain);
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
state.ppm_error = atof(optarg);
|
||||
printf("DBG: PPM Error = %.2f\n", state.ppm_error);
|
||||
break;
|
||||
case 'n':
|
||||
state.samples_to_read = atoi(optarg);
|
||||
state.sample_infinite = state.samples_to_read > 0 ? 0 : 1;
|
||||
|
||||
if (state.sample_infinite) printf("DBG: Infinite Read\n");
|
||||
else printf("DBG: Number of samples: %lu\n", state.samples_to_read);
|
||||
break;
|
||||
case 'F':
|
||||
state.force_fpga_prog = 1;
|
||||
printf("DBG: Force FPGA programming = %d\n", state.force_fpga_prog);
|
||||
break;
|
||||
case 'M':
|
||||
state.write_metadata = 1;
|
||||
printf("DBG: Write metadata = %d\n", state.write_metadata);
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
return -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (argc <= optind)
|
||||
{
|
||||
usage();
|
||||
return -1;
|
||||
}
|
||||
else state.filename = argv[optind];
|
||||
return 0;
|
||||
}
|
||||
|
||||
//=================================================
|
||||
void release_system(void)
|
||||
{
|
||||
cariboulite_radio_activate_channel(state.radio, cariboulite_channel_dir_rx, false);
|
||||
if (state.buffer) free (state.buffer);
|
||||
if (state.metadata) free (state.metadata);
|
||||
if (strcmp(state.filename, "-") != 0)
|
||||
{
|
||||
if (state.file) fclose(state.file);
|
||||
}
|
||||
cariboulite_close();
|
||||
}
|
||||
|
||||
//=================================================
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// pre-init the program state
|
||||
//-------------------------------------
|
||||
init_program_state();
|
||||
|
||||
// Analyze program opts
|
||||
//-------------------------------------
|
||||
if (analyze_arguments(argc, argv) != 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Init the program
|
||||
//-------------------------------------
|
||||
if (cariboulite_init(state.force_fpga_prog, cariboulite_log_level_none) != 0)
|
||||
{
|
||||
ZF_LOGE("driver init failed, terminating...");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// setup the signal handler
|
||||
cariboulite_register_signal_handler ( sighandler, &cariboulite_sys);
|
||||
|
||||
// check the input arguments (done after init to identify system type)
|
||||
if (check_inputs() != 0)
|
||||
{
|
||||
cariboulite_close();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// get the correct radio from the possible two
|
||||
state.radio = cariboulite_get_radio((cariboulite_channel_en)(state.rx_channel));
|
||||
|
||||
// Allocate rx buffer and metadata
|
||||
state.native_read_len = cariboulite_radio_get_native_mtu_size_samples(state.radio);
|
||||
state.buffer = malloc(sizeof(cariboulite_sample_complex_int16)*state.native_read_len);
|
||||
if (state.buffer == NULL)
|
||||
{
|
||||
ZF_LOGE("RX Buffer allocation failed");
|
||||
release_system();
|
||||
return -1;
|
||||
}
|
||||
|
||||
state.metadata = malloc(sizeof(cariboulite_sample_meta)*state.native_read_len);
|
||||
if (state.metadata == NULL)
|
||||
{
|
||||
ZF_LOGE("Metadata allocation failed");
|
||||
release_system();
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Align the length (only if it is >0)
|
||||
if (!state.sample_infinite)
|
||||
{
|
||||
state.samples_to_read = ((state.samples_to_read % state.native_read_len) == 0) ?
|
||||
(state.samples_to_read) :
|
||||
(state.samples_to_read / state.native_read_len + 1) * state.native_read_len;
|
||||
}
|
||||
|
||||
// Init the radio
|
||||
//-------------------------------------
|
||||
// Set radio parameters
|
||||
cariboulite_radio_set_frequency(state.radio, true, &state.frequency);
|
||||
cariboulite_radio_set_rx_gain_control(state.radio, state.gain == -1.0, state.gain);
|
||||
cariboulite_radio_sync_information(state.radio);
|
||||
cariboulite_radio_activate_channel(state.radio, cariboulite_channel_dir_rx, true);
|
||||
|
||||
// Open the file for writing
|
||||
if(strcmp(state.filename, "-") == 0)
|
||||
{
|
||||
state.file = stdout;
|
||||
}
|
||||
else
|
||||
{
|
||||
state.file = fopen(state.filename, "wb");
|
||||
if (!state.file)
|
||||
{
|
||||
ZF_LOGE("Failed to open %s", state.filename);
|
||||
release_system();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
usleep(100000);
|
||||
while (state.program_running)
|
||||
{
|
||||
int ret = cariboulite_radio_read_samples(state.radio, state.buffer, state.metadata, state.native_read_len);
|
||||
if (ret < 0)
|
||||
{
|
||||
ZF_LOGE("Samples read operation failed. Quiting...");
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: how should the metadata be expressed in the file?
|
||||
int wret = fwrite(state.buffer, 1, ret*4, state.file);
|
||||
if (wret != (ret*4))
|
||||
{
|
||||
ZF_LOGE("Writing into file failed, exiting!\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (!state.sample_infinite)
|
||||
{
|
||||
state.samples_to_read -= ret;
|
||||
if (state.samples_to_read <= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// close the driver and release resources
|
||||
cariboulite_close();
|
||||
return 0;
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
# build directories
|
||||
build
|
|
@ -1,30 +0,0 @@
|
|||
cmake_minimum_required(VERSION 3.15)
|
||||
project(cariboulite)
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
|
||||
#Bring the headers, such as Student.h into the project
|
||||
set(SUPER_DIR ${PROJECT_SOURCE_DIR}/..)
|
||||
include_directories(/.)
|
||||
include_directories(${SUPER_DIR})
|
||||
|
||||
#However, the file(GLOB...) allows for wildcard additions:
|
||||
set(SOURCES_LIB tsqueue.c tiny_list.c circular_buffer.cpp entropy.c)
|
||||
#add_compile_options(-Wall -Wextra -pedantic -Werror)
|
||||
add_compile_options(-Wall -Wextra -pedantic -Wno-missing-braces)
|
||||
|
||||
#Generate the static library from the sources
|
||||
add_library(datatypes STATIC ${SOURCES_LIB})
|
||||
target_include_directories(datatypes PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
#add_executable(test_tsqueue test_tsqueue.c)
|
||||
#target_link_libraries(test_tsqueue datatypes pthread)
|
||||
|
||||
#add_executable(test_circular_buffer test_circular_buffer.cpp)
|
||||
#target_link_libraries(test_circular_buffer datatypes pthread)
|
||||
|
||||
add_executable(test_tiny_list test_tiny_list.c)
|
||||
target_link_libraries(test_tiny_list datatypes pthread)
|
||||
|
||||
#Set the location for library installation -- i.e., /usr/lib in this case
|
||||
# not really necessary in this example. Use "sudo make install" to apply
|
||||
install(TARGETS datatypes DESTINATION /usr/lib)
|
|
@ -1 +0,0 @@
|
|||
#include "circular_buffer.h"
|
|
@ -1,165 +0,0 @@
|
|||
#ifndef __CIRC_BUFFER_H__
|
||||
#define __CIRC_BUFFER_H__
|
||||
|
||||
#include <string.h>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
|
||||
#define IS_POWER_OF_2(x) (!((x) == 0) && !((x) & ((x) - 1)))
|
||||
#define MIN(x,y) ((x)>(y)?(y):(x))
|
||||
|
||||
|
||||
template <class T>
|
||||
class circular_buffer {
|
||||
public:
|
||||
circular_buffer(size_t size, bool override_write = true, bool block_read = true)
|
||||
{
|
||||
max_size_ = size;
|
||||
if (!IS_POWER_OF_2(max_size_))
|
||||
{
|
||||
max_size_ = next_power_of_2(max_size_);
|
||||
}
|
||||
buf_ = new T[max_size_];
|
||||
override_write_ = override_write;
|
||||
block_read_ = block_read;
|
||||
}
|
||||
|
||||
~circular_buffer()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
delete []buf_;
|
||||
}
|
||||
|
||||
size_t put(const T *data, size_t length)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
if ((max_size_ - size()) < length && override_write_)
|
||||
{
|
||||
// pop the amount of data the is needed
|
||||
tail_ += length - (max_size_ - size());
|
||||
}
|
||||
|
||||
size_t len = MIN(length, max_size_ - head_ + tail_);
|
||||
auto l = MIN(len, max_size_ - (head_ & (max_size_ - 1)));
|
||||
|
||||
memcpy(buf_ + (head_ & (max_size_ - 1)), data, l * sizeof(T));
|
||||
memcpy(buf_, data + l, (len - l) * sizeof(T));
|
||||
|
||||
head_ += len;
|
||||
|
||||
if (block_read_)
|
||||
{
|
||||
cond_var_.notify_one();
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t get(T *data, size_t length, int timeout_us = 100000)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
|
||||
if (block_read_)
|
||||
{
|
||||
cond_var_.wait_for(lock, std::chrono::microseconds(timeout_us), [&]()
|
||||
{
|
||||
// Acquire the lock only if
|
||||
// we got enough items
|
||||
return size() >= length;
|
||||
});
|
||||
|
||||
if (size() < length)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
size_t len = MIN(length, head_ - tail_);
|
||||
auto l = MIN(len, max_size_ - (tail_ & (max_size_ - 1)));
|
||||
|
||||
if (data != NULL)
|
||||
{
|
||||
memcpy(data, buf_ + (tail_ & (max_size_ - 1)), l * sizeof(T));
|
||||
memcpy(data + l, buf_, (len - l) * sizeof(T));
|
||||
}
|
||||
tail_ += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
void put(T item)
|
||||
{
|
||||
put(&item, 1);
|
||||
}
|
||||
|
||||
T get()
|
||||
{
|
||||
T item;
|
||||
get(&item, 1);
|
||||
return item;
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
head_ = tail_ = 0;
|
||||
}
|
||||
|
||||
inline bool empty()
|
||||
{
|
||||
return head_ == tail_;
|
||||
}
|
||||
|
||||
inline bool full()
|
||||
{
|
||||
return size() == capacity();
|
||||
}
|
||||
|
||||
inline size_t capacity() const
|
||||
{
|
||||
return max_size_;
|
||||
}
|
||||
|
||||
size_t size()
|
||||
{
|
||||
return (head_ - tail_);
|
||||
}
|
||||
|
||||
void print_buffer()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mutex_);
|
||||
size_t t = tail_;
|
||||
int i = 0;
|
||||
while (t < head_)
|
||||
{
|
||||
printf("%d => %d\n", i++, (int)buf_[t++&(max_size_-1)]);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t next_power_of_2 (uint32_t x)
|
||||
{
|
||||
uint32_t power = 1;
|
||||
while(power < x)
|
||||
{
|
||||
power <<= 1;
|
||||
}
|
||||
return power;
|
||||
}
|
||||
|
||||
private:
|
||||
std::mutex mutex_;
|
||||
std::condition_variable cond_var_;
|
||||
|
||||
T* buf_;
|
||||
size_t head_ = 0;
|
||||
size_t tail_ = 0;
|
||||
size_t max_size_;
|
||||
bool override_write_;
|
||||
bool block_read_;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,57 +0,0 @@
|
|||
#ifndef ZF_LOG_LEVEL
|
||||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#endif
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "ENTROPY"
|
||||
#include "zf_log/zf_log.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <linux/random.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include "entropy.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
int bit_count; /* number of bits of entropy in data */
|
||||
int byte_count; /* number of bytes of data in array */
|
||||
unsigned char buf[1];
|
||||
} entropy_t;
|
||||
|
||||
//=====================================================
|
||||
int add_entropy(uint8_t byte)
|
||||
{
|
||||
int rand_fid = open("/dev/urandom", O_RDWR);
|
||||
if (rand_fid != 0)
|
||||
{
|
||||
// error opening device
|
||||
ZF_LOGE("Opening /dev/urandom device file failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
entropy_t ent =
|
||||
{
|
||||
.bit_count = 8,
|
||||
.byte_count = 1,
|
||||
.buf = {byte},
|
||||
};
|
||||
|
||||
if (ioctl(rand_fid, RNDADDENTROPY, &ent) != 0)
|
||||
{
|
||||
ZF_LOGE("IOCTL to /dev/urandom device file failed");
|
||||
}
|
||||
|
||||
if (close(rand_fid) !=0 )
|
||||
{
|
||||
ZF_LOGE("Closing /dev/urandom device file failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
#ifndef __ENTROPY_H__
|
||||
#define __ENTROPY_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
int add_entropy(uint8_t byte);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __ENTROPY_H__
|
|
@ -1,55 +0,0 @@
|
|||
#include "circular_buffer.h"
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
circular_buffer<uint32_t> *buf = NULL;
|
||||
|
||||
uint32_t data1[100] = {0};
|
||||
uint32_t data2[100] = {0};
|
||||
|
||||
void producer(int times)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
sleep(0);
|
||||
printf("THREAD! PUT1 100, ret %lu\n", buf->put(data1, 100));
|
||||
sleep(0);
|
||||
printf("PUT1 60, ret %lu\n", buf->put(data1, 60));
|
||||
}
|
||||
}
|
||||
|
||||
void consumer(int times)
|
||||
{
|
||||
printf("Capacity = %lu\n", buf->capacity());
|
||||
while (true)
|
||||
{
|
||||
printf("GET1 100, ret %lu\n", buf->get(data2, 100));
|
||||
printf("GET1 100, ret %lu\n", buf->get(data2, 100));
|
||||
printf("GET1 100, ret %lu\n", buf->get(data2, 100));
|
||||
printf("GET1 100, ret %lu\n", buf->get(data2, 60));
|
||||
}
|
||||
|
||||
printf("finished\n");
|
||||
}
|
||||
|
||||
int main ()
|
||||
{
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
data1[i] = i;
|
||||
data2[i] = i*i;
|
||||
}
|
||||
|
||||
buf = new circular_buffer<uint32_t>(200, true, true);
|
||||
|
||||
int times = 1;
|
||||
std::thread t1(producer, times);
|
||||
std::thread t2(consumer, times);
|
||||
|
||||
t2.join();
|
||||
t1.join();
|
||||
|
||||
delete buf;
|
||||
return 0;
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include "tiny_list.h"
|
||||
#include "tiny_queue.h"
|
||||
|
||||
int a = 0;
|
||||
|
||||
tiny_queue_st queue;
|
||||
|
||||
int main ()
|
||||
{
|
||||
tiny_queue_init (&queue);
|
||||
|
||||
tiny_queue_enqueue (queue, &a, sizeof (a)); a++;
|
||||
tiny_queue_enqueue (queue, &a, sizeof (a)); a++;
|
||||
tiny_queue_enqueue (queue, &a, sizeof (a)); a++;
|
||||
|
||||
tiny_list_print (queue);
|
||||
|
||||
tiny_queue_dequeue (queue, &a, NULL);
|
||||
tiny_list_print (queue);
|
||||
tiny_queue_dequeue (queue, &a, NULL);
|
||||
tiny_list_print (queue);
|
||||
tiny_queue_dequeue (queue, &a, NULL);
|
||||
tiny_list_print (queue);
|
||||
|
||||
tiny_queue_free (queue);
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include "tsqueue.h"
|
||||
|
||||
tsqueue_st q = {0};
|
||||
pthread_t tid;
|
||||
pthread_attr_t attr;
|
||||
#define RAND_DIVISOR 1000000000
|
||||
|
||||
// Producer Thread
|
||||
void *producer(void *param)
|
||||
{
|
||||
int item = 0;
|
||||
|
||||
while(1)
|
||||
{
|
||||
// sleep for a random period of time
|
||||
/*int rNum = rand() / RAND_DIVISOR;
|
||||
sleep(rNum);
|
||||
*/
|
||||
|
||||
//getchar();
|
||||
|
||||
// generate a random number
|
||||
//item = rand();
|
||||
item ++;
|
||||
|
||||
int res = tsqueue_insert_push_buffer(&q, (uint8_t*)&item, sizeof(item), 0, 200, 1);
|
||||
if (res != 0)
|
||||
{
|
||||
if (res == TSQUEUE_NOT_INITIALIZED) fprintf(stderr, "Queue not inited\n");
|
||||
if (res == TSQUEUE_FAILED_FULL) fprintf(stderr, "Queue is full!\n");
|
||||
if (res == TSQUEUE_SEM_FAILED) fprintf(stderr, "Push semaphore failed\n");
|
||||
if (res == TSQUEUE_TIMEOUT) fprintf(stderr, "Push TIMEOUT\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("producer produced and pushed %d\n", item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Consumer Thread
|
||||
void *consumer(void *param)
|
||||
{
|
||||
tsqueue_item_st* item = NULL;
|
||||
|
||||
int i = 0;
|
||||
while(1)
|
||||
{
|
||||
/* sleep for a random period of time */
|
||||
//int rNum = rand() / RAND_DIVISOR;
|
||||
//sleep(1);
|
||||
|
||||
//getchar();
|
||||
int res;
|
||||
if (i==0) res = tsqueue_pop_item(&q, &item, 200);
|
||||
else res = tsqueue_peek_item(&q, &item, 200);
|
||||
|
||||
if (i==0) printf("POP ==> ");
|
||||
else printf("PEEK ==> ");
|
||||
if (res != 0)
|
||||
{
|
||||
if (res == TSQUEUE_NOT_INITIALIZED) fprintf(stderr, "Queue not inited\n");
|
||||
if (res == TSQUEUE_SEM_FAILED) fprintf(stderr, "Pop semaphore failed\n");
|
||||
if (res == TSQUEUE_FAILED_EMPTY) fprintf(stderr, "Pop failed - Empty!!\n");
|
||||
if (res == TSQUEUE_TIMEOUT) fprintf(stderr, "Pop TIMEOUT\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
int val = *((uint32_t*)item->data);
|
||||
printf("consumer consumed %d\n", val);
|
||||
}
|
||||
i = !i;
|
||||
}
|
||||
}
|
||||
|
||||
// Main
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
if(argc != 4)
|
||||
{
|
||||
fprintf(stderr, "USAGE:./main.out <INT> <INT> <INT>\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int mainSleepTime = atoi(argv[1]); /* Time in seconds for main to sleep */
|
||||
int numProd = atoi(argv[2]); /* Number of producer threads */
|
||||
int numCons = atoi(argv[3]); /* Number of consumer threads */
|
||||
|
||||
res = tsqueue_init(&q, 4, 4);
|
||||
|
||||
// Create the producer threads
|
||||
for(int i = 0; i < numProd; i++)
|
||||
{
|
||||
pthread_create(&tid, &attr, producer,NULL);
|
||||
}
|
||||
|
||||
// Create the consumer threads
|
||||
for(int i = 0; i < numCons; i++)
|
||||
{
|
||||
pthread_create(&tid, &attr, consumer,NULL);
|
||||
}
|
||||
|
||||
sleep(mainSleepTime);
|
||||
printf("Exit the program\n");
|
||||
|
||||
res = tsqueue_release(&q);
|
||||
return 0;
|
||||
}
|
|
@ -1,278 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
#include "tiny_list.h"
|
||||
|
||||
//============================================================
|
||||
int tiny_list_init (tiny_list_st** list)
|
||||
{
|
||||
tiny_list_st* new_list = (tiny_list_st*)malloc(sizeof (tiny_list_st));
|
||||
if (new_list == NULL)
|
||||
{
|
||||
printf("'tiny_list_init' - allocation failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pthread_mutex_init(&new_list->mtx, NULL) != 0)
|
||||
{
|
||||
printf("'tiny_list_init' - mutex initialization failed\n");
|
||||
free (new_list);
|
||||
return -1;
|
||||
}
|
||||
pthread_mutex_unlock (&new_list->mtx);
|
||||
|
||||
new_list->head = NULL;
|
||||
new_list->tail = NULL;
|
||||
new_list->num_of_elements = 0;
|
||||
|
||||
*list = new_list;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
void tiny_list_free (tiny_list_st* list)
|
||||
{
|
||||
tiny_list_remove_all (list);
|
||||
free (list);
|
||||
}
|
||||
|
||||
//============================================================
|
||||
int tiny_list_add (tiny_list_st* list, void* data, unsigned int len, tiny_list_pos_en pos)
|
||||
{
|
||||
if (list == NULL)
|
||||
{
|
||||
printf("'tiny_list_add' - list not initialized\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
tiny_list_node_st* new_node = tiny_list_create_node (data, len);
|
||||
if (new_node == NULL)
|
||||
{
|
||||
printf("'tiny_list_add' - node creation failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pthread_mutex_lock (&list->mtx);
|
||||
if (pos == pos_head)
|
||||
{
|
||||
new_node->next = list->head;
|
||||
new_node->last = NULL;
|
||||
list->head = new_node;
|
||||
if (new_node->next) new_node->next->last = new_node;
|
||||
if (list->num_of_elements == 0) list->tail = list->head;
|
||||
}
|
||||
else // default = tail
|
||||
{
|
||||
new_node->next = NULL;
|
||||
new_node->last = list->tail;
|
||||
list->tail = new_node;
|
||||
|
||||
if (new_node->last) new_node->last->next = new_node;
|
||||
if (list->num_of_elements == 0) list->head = list->tail;
|
||||
}
|
||||
|
||||
list->num_of_elements ++;
|
||||
|
||||
pthread_mutex_unlock (&list->mtx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
int tiny_list_remove (tiny_list_st* list, void* data, unsigned int *len, tiny_list_pos_en pos)
|
||||
{
|
||||
tiny_list_node_st* node = NULL;
|
||||
if (list == NULL)
|
||||
{
|
||||
printf("'tiny_list_remove' - list not initialized\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pthread_mutex_lock (&list->mtx);
|
||||
if (list->num_of_elements == 0)
|
||||
{
|
||||
printf("'tiny_list_remove' - the list is empty - nothing to remove\n");
|
||||
pthread_mutex_unlock (&list->mtx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pos == pos_head)
|
||||
{
|
||||
node = list->head;
|
||||
list->head = node->next;
|
||||
if (list->head) list->head->last = NULL;
|
||||
}
|
||||
else // default = tail
|
||||
{
|
||||
node = list->tail;
|
||||
list->tail = node->last;
|
||||
if (list->tail) list->tail->next = NULL;
|
||||
}
|
||||
|
||||
list->num_of_elements --;
|
||||
if (list->num_of_elements == 0)
|
||||
{
|
||||
list->tail = NULL;
|
||||
list->head = NULL;
|
||||
}
|
||||
pthread_mutex_unlock (&list->mtx);
|
||||
|
||||
if (len) *len = node->len;
|
||||
if (data) memcpy(data, node->data, node->len);
|
||||
|
||||
tiny_list_destroy_node (node);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
void tiny_list_remove_all (tiny_list_st* list)
|
||||
{
|
||||
tiny_list_node_st* node = NULL;
|
||||
if (list == NULL)
|
||||
{
|
||||
printf("'tiny_list_free' - trying to free not initialized list\n");
|
||||
return;
|
||||
}
|
||||
|
||||
node = list->head;
|
||||
pthread_mutex_unlock (&list->mtx);
|
||||
pthread_mutex_destroy (&list->mtx);
|
||||
|
||||
while (node != NULL)
|
||||
{
|
||||
node = tiny_list_destroy_node (node);
|
||||
}
|
||||
|
||||
list->num_of_elements = 0;
|
||||
list->head = NULL;
|
||||
list->tail = NULL;
|
||||
|
||||
}
|
||||
|
||||
//============================================================
|
||||
int tiny_list_peek (tiny_list_st* list, void* data, unsigned int *len, tiny_list_pos_en pos)
|
||||
{
|
||||
tiny_list_node_st* node = NULL;
|
||||
if (list == NULL)
|
||||
{
|
||||
printf("'tiny_list_peek' - list not initialized\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pthread_mutex_lock (&list->mtx);
|
||||
if (list->num_of_elements == 0)
|
||||
{
|
||||
printf("'tiny_list_remove' - the list is empty - nothing to peek\n");
|
||||
pthread_mutex_unlock (&list->mtx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (pos == pos_head)
|
||||
{
|
||||
node = list->head;
|
||||
}
|
||||
else // default = tail
|
||||
{
|
||||
node = list->tail;
|
||||
}
|
||||
|
||||
if (len) *len = node->len;
|
||||
if (data) memcpy(data, node->data, node->len);
|
||||
|
||||
pthread_mutex_unlock (&list->mtx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
int tiny_list_num_elements (tiny_list_st* list)
|
||||
{
|
||||
int num;
|
||||
if (list == NULL)
|
||||
{
|
||||
printf("'tiny_list_num_elements' - list not initialized\n");
|
||||
return -1;
|
||||
}
|
||||
pthread_mutex_lock (&list->mtx);
|
||||
num = list->num_of_elements;
|
||||
pthread_mutex_unlock (&list->mtx);
|
||||
return num;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
void tiny_list_print (tiny_list_st* list)
|
||||
{
|
||||
tiny_list_node_st* cur_node;
|
||||
if (list == NULL)
|
||||
{
|
||||
printf("'tiny_list_print' - list is not initialized\n");
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_lock (&list->mtx);
|
||||
printf("Number of elements: %d - \n", list->num_of_elements);
|
||||
cur_node = list->head;
|
||||
while (cur_node)
|
||||
{
|
||||
tiny_list_print_node (cur_node);
|
||||
cur_node = cur_node->next;
|
||||
}
|
||||
pthread_mutex_unlock (&list->mtx);
|
||||
}
|
||||
|
||||
|
||||
//============================================================
|
||||
tiny_list_node_st* tiny_list_create_node (void *data, unsigned int len)
|
||||
{
|
||||
tiny_list_node_st* new_node = (tiny_list_node_st*)malloc (sizeof (tiny_list_node_st));
|
||||
if (new_node == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
new_node->data = malloc (len);
|
||||
if (new_node->data == NULL)
|
||||
{
|
||||
free (new_node);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memcpy (new_node->data, data, len);
|
||||
new_node->len = len;
|
||||
new_node->next = NULL;
|
||||
new_node->last = NULL;
|
||||
return new_node;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
tiny_list_node_st* tiny_list_destroy_node (tiny_list_node_st* node)
|
||||
{
|
||||
tiny_list_node_st* temp = NULL;
|
||||
if (node == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
temp = node->next;
|
||||
|
||||
free (node->data);
|
||||
node->len = 0;
|
||||
node->next = NULL;
|
||||
node->last = NULL;
|
||||
free (node);
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
//============================================================
|
||||
void tiny_list_print_node (tiny_list_node_st* node)
|
||||
{
|
||||
if (node == NULL)
|
||||
{
|
||||
printf("empty node\n");
|
||||
return;
|
||||
}
|
||||
|
||||
printf(" PTR ADDR: %08lX, FIRST BYTE: %02X, LEN: %d\n", (long)(node->data), ((unsigned char*)(node->data))[0], node->len);
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
#ifndef __TINY_LIST_H__
|
||||
#define __TINY_LIST_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
typedef enum
|
||||
{
|
||||
pos_head = 0,
|
||||
pos_tail = 1,
|
||||
} tiny_list_pos_en;
|
||||
|
||||
struct tiny_list_node_t
|
||||
{
|
||||
void* data;
|
||||
unsigned int len;
|
||||
|
||||
struct tiny_list_node_t *last;
|
||||
struct tiny_list_node_t *next;
|
||||
};
|
||||
|
||||
typedef struct tiny_list_node_t tiny_list_node_st;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
pthread_mutex_t mtx;
|
||||
struct tiny_list_node_t* head;
|
||||
struct tiny_list_node_t* tail;
|
||||
|
||||
unsigned int num_of_elements;
|
||||
} tiny_list_st;
|
||||
|
||||
|
||||
int tiny_list_init (tiny_list_st** list);
|
||||
void tiny_list_free (tiny_list_st* list);
|
||||
int tiny_list_add (tiny_list_st* list, void* data, unsigned int len, tiny_list_pos_en pos);
|
||||
int tiny_list_remove (tiny_list_st* list, void* data, unsigned int *len, tiny_list_pos_en pos);
|
||||
void tiny_list_remove_all (tiny_list_st* list);
|
||||
int tiny_list_peek (tiny_list_st* list, void* data, unsigned int *len, tiny_list_pos_en pos);
|
||||
int tiny_list_num_elements (tiny_list_st* list);
|
||||
void tiny_list_print (tiny_list_st* list);
|
||||
tiny_list_node_st* tiny_list_create_node (void *data, unsigned int len);
|
||||
tiny_list_node_st* tiny_list_destroy_node (tiny_list_node_st* node);
|
||||
void tiny_list_print_node (tiny_list_node_st* node);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //__TINY_LIST_H__
|
|
@ -1,25 +0,0 @@
|
|||
#ifndef __TINY_QUEUE_H__
|
||||
#define __TINY_QUEUE_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include "tiny_list.h"
|
||||
|
||||
typedef tiny_list_st* tiny_queue_st;
|
||||
|
||||
#define tiny_queue_init(q_ptr) (tiny_list_init(q_ptr))
|
||||
#define tiny_queue_free(q) (tiny_list_free(q))
|
||||
#define tiny_queue_enqueue(q,d_array,l) (tiny_list_add((q),(d_array),(l),pos_head))
|
||||
#define tiny_queue_dequeue(q,d_array,l_ptr) (tiny_list_remove((q),(d_array),(l_ptr),pos_tail))
|
||||
#define tiny_queue_is_empty(q) (tiny_list_num_elements(q)==0)
|
||||
#define tiny_queue_num_elements(q) (tiny_list_num_elements(q))
|
||||
#define tiny_queue_empty(q) (tiny_list_remove_all (q))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //__TINY_QUEUE_H__
|
|
@ -1,24 +0,0 @@
|
|||
#ifndef __TINY_STACK_H__
|
||||
#define __TINY_STACK_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include "tiny_list.h"
|
||||
|
||||
typedef tiny_list_st* tiny_stack_st;
|
||||
|
||||
#define tiny_stack_init(s_ptr) (tiny_list_init(s_ptr))
|
||||
#define tiny_stack_free(s) (tiny_list_free(s))
|
||||
#define tiny_stack_enqueue(s,d_array,l) (tiny_list_add((s),(d_array),(l),pos_head))
|
||||
#define tiny_stack_dequeue(s,d_array,l_ptr) (tiny_list_remove((s),(d_array),(l_ptr),pos_head))
|
||||
#define tiny_stack_is_empty(s) (tiny_list_num_elements(s)==0)
|
||||
#define tiny_stack_empty(s) (tiny_list_remove_all (s))
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //__TINY_STACK_H__
|
|
@ -1,309 +0,0 @@
|
|||
#include "tsqueue.h"
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
|
||||
//=======================================================================
|
||||
int tsqueue_init(tsqueue_st* q, int item_size_bytes, int total_element_count)
|
||||
{
|
||||
if (q == NULL) return -1;
|
||||
if (item_size_bytes == 0 || total_element_count == 0) return -2;
|
||||
|
||||
q->max_num_items = total_element_count;
|
||||
q->item_size_bytes = item_size_bytes;
|
||||
|
||||
q->sema_inited = 0;
|
||||
if (pthread_mutex_init(&q->mutex, NULL) != 0)
|
||||
{
|
||||
//printf("tsqueue1\n");
|
||||
return TSQUEUE_MUTEX_INIT_FAILED;
|
||||
}
|
||||
|
||||
if (sem_init(&q->full, 0, 0) != 0)
|
||||
{
|
||||
//printf("tsqueue2\n");
|
||||
pthread_mutex_destroy(&q->mutex);
|
||||
return TSQUEUE_MUTEX_INIT_FAILED;
|
||||
}
|
||||
|
||||
if (sem_init(&q->empty, 0, total_element_count) != 0)
|
||||
{
|
||||
//printf("tsqueue3\n");
|
||||
sem_destroy(&q->full);
|
||||
pthread_mutex_destroy(&q->mutex);
|
||||
return TSQUEUE_SEM_INIT_FAILED;
|
||||
}
|
||||
|
||||
q->sema_inited = 1;
|
||||
|
||||
q->items = (tsqueue_item_st*)malloc(total_element_count*sizeof(tsqueue_item_st));
|
||||
if (q->items == NULL)
|
||||
{
|
||||
//printf("tsqueue4\n");
|
||||
return TSQUEUE_MEM_ALLOC_FAILED;
|
||||
}
|
||||
|
||||
memset (q->items, 0, total_element_count*sizeof(tsqueue_item_st));
|
||||
|
||||
for (int i = 0; i < total_element_count; i++)
|
||||
{
|
||||
q->items[i].metadata = 0;
|
||||
q->items[i].data = (uint8_t*)malloc(q->item_size_bytes);
|
||||
if (q->items[i].data == NULL)
|
||||
{
|
||||
//printf("tsqueue5 %d\n", i);
|
||||
tsqueue_release(q);
|
||||
return TSQUEUE_MEM_ALLOC_FAILED;
|
||||
}
|
||||
q->items[i].length = 0;
|
||||
}
|
||||
|
||||
q->current_num_of_items = 0;
|
||||
q->head = 0;
|
||||
q->tail = 0;
|
||||
q->num_items_dropped = 0;
|
||||
return TSQUEUE_OK;
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
int tsqueue_release(tsqueue_st* q)
|
||||
{
|
||||
if (q == NULL) return -1;
|
||||
for (int i = 0; i < q->max_num_items; i++)
|
||||
{
|
||||
if (q->items[i].data != NULL) free (q->items[i].data);
|
||||
}
|
||||
free(q->items);
|
||||
|
||||
if (q->sema_inited)
|
||||
{
|
||||
sem_destroy(&q->empty);
|
||||
sem_destroy(&q->full);
|
||||
pthread_mutex_destroy(&q->mutex);
|
||||
}
|
||||
return TSQUEUE_OK;
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
int tsqueue_insert_push_item(tsqueue_st* q, tsqueue_item_st* item, int timeout_us, int override)
|
||||
{
|
||||
return tsqueue_insert_push_buffer(q, item->data, item->length, item->metadata, timeout_us, override);
|
||||
}
|
||||
|
||||
|
||||
//=======================================================================
|
||||
void tsqueue_add_ms_to_timespec(struct timespec *ts, int us)
|
||||
{
|
||||
long int sec = ts->tv_sec;
|
||||
long int nsec = ts->tv_nsec;
|
||||
long int added_ns = us * 1000L;
|
||||
nsec += added_ns;
|
||||
int added_sec = nsec / 1000000000L;
|
||||
sec += added_sec;
|
||||
nsec -= added_sec * 1000000000L;
|
||||
ts->tv_nsec = nsec;
|
||||
ts->tv_sec = sec;
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
int tsqueue_wait_on_sem(sem_t* sem, int timeout_us)
|
||||
{
|
||||
// check if timeout is needed
|
||||
if (timeout_us <= 0)
|
||||
{
|
||||
int s = 0;
|
||||
while ((s = sem_wait(sem)) == -1 && errno == EINTR) continue;
|
||||
if (s == -1) return TSQUEUE_SEM_FAILED;
|
||||
}
|
||||
else
|
||||
{
|
||||
struct timespec ts = {0};
|
||||
int s = 0;
|
||||
|
||||
// get current time
|
||||
clock_gettime(CLOCK_REALTIME, &ts);
|
||||
tsqueue_add_ms_to_timespec(&ts, timeout_us);
|
||||
while ((s = sem_timedwait(sem, &ts)) == -1 && errno == EINTR)
|
||||
{
|
||||
continue; /* Restart if interrupted by handler. */
|
||||
}
|
||||
|
||||
// Check what happened - it was not an interruption
|
||||
if (s == -1)
|
||||
{
|
||||
if (errno == ETIMEDOUT)
|
||||
{
|
||||
return TSQUEUE_TIMEOUT;
|
||||
}
|
||||
else
|
||||
{
|
||||
return TSQUEUE_SEM_FAILED;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
int tsqueue_insert_push_buffer(tsqueue_st* q, uint8_t* buffer, int length, uint32_t metadata, int timeout_us, int override)
|
||||
{
|
||||
int error = 0;
|
||||
if (q == NULL) return TSQUEUE_NOT_INITIALIZED;
|
||||
|
||||
if (!override)
|
||||
{
|
||||
// wait until empty rises => someone poped an item thus some place was
|
||||
// freed for a new item. immediatelly after catching the event, decrease empty (notice it is full)
|
||||
// in the case of override - no waiting is needed
|
||||
int res = tsqueue_wait_on_sem(&q->empty, timeout_us);
|
||||
if (res < 0) return res;
|
||||
}
|
||||
|
||||
// acquire the mutex lock
|
||||
pthread_mutex_lock(&q->mutex);
|
||||
|
||||
// insert the item
|
||||
// When the buffer is not full add the item and increment the counter
|
||||
if(q->current_num_of_items < q->max_num_items)
|
||||
{
|
||||
int data_size = length;
|
||||
q->items[q->head].metadata = metadata;
|
||||
if (data_size > q->item_size_bytes)
|
||||
{
|
||||
printf("Incorrect item data size\n");
|
||||
data_size = q->item_size_bytes;
|
||||
}
|
||||
memcpy (q->items[q->head].data, buffer, data_size);
|
||||
q->items[q->head].length = data_size;
|
||||
q->head ++;
|
||||
if (q->head >= q->max_num_items) q->head = 0;
|
||||
q->current_num_of_items ++;
|
||||
}
|
||||
else if (override)
|
||||
{
|
||||
int res = tsqueue_wait_on_sem(&q->full, timeout_us);
|
||||
if (res < 0) return res;
|
||||
|
||||
int data_size = length;
|
||||
q->items[q->head].metadata = metadata;
|
||||
if (data_size > q->item_size_bytes)
|
||||
{
|
||||
printf("Incorrect item data size\n");
|
||||
data_size = q->item_size_bytes;
|
||||
}
|
||||
memcpy (q->items[q->head].data, buffer, data_size);
|
||||
q->items[q->head].length = data_size;
|
||||
q->head ++;
|
||||
if (q->head >= q->max_num_items) q->head = 0;
|
||||
q->tail ++;
|
||||
if (q->tail >= q->max_num_items) q->tail = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
error = TSQUEUE_FAILED_FULL;
|
||||
}
|
||||
|
||||
// release the mutex lock
|
||||
pthread_mutex_unlock(&q->mutex);
|
||||
|
||||
// signal full
|
||||
sem_post(&q->full);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
int tsqueue_peek_item(tsqueue_st* q, tsqueue_item_st** item, int timeout_us)
|
||||
{
|
||||
int error = 0;
|
||||
if (q == NULL) return TSQUEUE_NOT_INITIALIZED;
|
||||
|
||||
int res = tsqueue_wait_on_sem(&q->full, timeout_us);
|
||||
if (res < 0) return res;
|
||||
|
||||
// aquire the mutex lock
|
||||
pthread_mutex_lock(&q->mutex);
|
||||
|
||||
// pop the item
|
||||
if(q->current_num_of_items > 0)
|
||||
{
|
||||
*item = &q->items[q->tail];
|
||||
}
|
||||
else
|
||||
{
|
||||
error = TSQUEUE_FAILED_EMPTY;
|
||||
}
|
||||
|
||||
// release the mutex lock
|
||||
pthread_mutex_unlock(&q->mutex);
|
||||
// signal full to note that we didn't pop the item
|
||||
sem_post(&q->full);
|
||||
return error;
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
int tsqueue_pop_item(tsqueue_st* q, tsqueue_item_st** item, int timeout_us)
|
||||
{
|
||||
int error = 0;
|
||||
if (q == NULL) return TSQUEUE_NOT_INITIALIZED;
|
||||
|
||||
int res = tsqueue_wait_on_sem(&q->full, timeout_us);
|
||||
if (res < 0) return res;
|
||||
|
||||
// aquire the mutex lock
|
||||
pthread_mutex_lock(&q->mutex);
|
||||
|
||||
// pop the item
|
||||
if(q->current_num_of_items > 0)
|
||||
{
|
||||
*item = &q->items[q->tail];
|
||||
q->tail ++;
|
||||
if (q->tail >= q->max_num_items) q->tail = 0;
|
||||
q->current_num_of_items --;
|
||||
}
|
||||
else
|
||||
{
|
||||
error = TSQUEUE_FAILED_EMPTY;
|
||||
}
|
||||
|
||||
// release the mutex lock
|
||||
pthread_mutex_unlock(&q->mutex);
|
||||
// signal empty
|
||||
sem_post(&q->empty);
|
||||
return error;
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
int tsqueue_number_of_items(tsqueue_st* q)
|
||||
{
|
||||
int num_items = 0;
|
||||
if (q == NULL) return TSQUEUE_NOT_INITIALIZED;
|
||||
// acquire the mutex lock
|
||||
pthread_mutex_lock(&q->mutex);
|
||||
|
||||
num_items = q->current_num_of_items;
|
||||
// release the mutex lock
|
||||
pthread_mutex_unlock(&q->mutex);
|
||||
return num_items;
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
int tsqueue_reset_dropped_counter(tsqueue_st* q)
|
||||
{
|
||||
if (q == NULL) return TSQUEUE_NOT_INITIALIZED;
|
||||
pthread_mutex_lock(&q->mutex);
|
||||
q->num_items_dropped = 0;
|
||||
pthread_mutex_unlock(&q->mutex);
|
||||
return TSQUEUE_OK;
|
||||
}
|
||||
|
||||
//=======================================================================
|
||||
int tsqueue_get_number_of_dropped(tsqueue_st* q)
|
||||
{
|
||||
int num_items = 0;
|
||||
if (q == NULL) return TSQUEUE_NOT_INITIALIZED;
|
||||
pthread_mutex_lock(&q->mutex);
|
||||
num_items = q->num_items_dropped;
|
||||
pthread_mutex_unlock(&q->mutex);
|
||||
return num_items;
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
#ifndef __TSQUEUE_H__
|
||||
#define __TSQUEUE_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define TSQUEUE_TIMEOUT (1)
|
||||
#define TSQUEUE_OK (0)
|
||||
#define TSQUEUE_BUFFER_FULL (-1)
|
||||
#define TSQUEUE_MEM_ALLOC_FAILED (-2)
|
||||
#define TSQUEUE_SEM_INIT_FAILED (-3)
|
||||
#define TSQUEUE_MUTEX_INIT_FAILED (-4)
|
||||
#define TSQUEUE_NOT_INITIALIZED (-5)
|
||||
#define TSQUEUE_FAILED_FULL (-6)
|
||||
#define TSQUEUE_SEM_FAILED (-7)
|
||||
#define TSQUEUE_FAILED_EMPTY (-8)
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t *data;
|
||||
int length;
|
||||
uint32_t metadata; // can be a pointer context, a number, a set of flags, etc.
|
||||
} tsqueue_item_st;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
tsqueue_item_st *items;
|
||||
int max_num_items;
|
||||
int item_size_bytes;
|
||||
int current_num_of_items;
|
||||
int head;
|
||||
int tail;
|
||||
|
||||
// The mutex lock, and the semaphores
|
||||
pthread_mutex_t mutex;
|
||||
sem_t full, empty;
|
||||
int sema_inited;
|
||||
|
||||
int num_items_dropped;
|
||||
} tsqueue_st;
|
||||
|
||||
/*
|
||||
* Init the queue
|
||||
* q: a pointer to a pre-allocated structure
|
||||
* item_size_bytes: the maximal container size of each item in the queue (bytes)
|
||||
* total_element_count: the total capacity of the cyclic queue
|
||||
*/
|
||||
int tsqueue_init(tsqueue_st* q, int item_size_bytes, int total_element_count);
|
||||
|
||||
/*
|
||||
* Release the queue
|
||||
*/
|
||||
int tsqueue_release(tsqueue_st* q);
|
||||
|
||||
/*
|
||||
* Push an item to the queue
|
||||
* item: the contents of the item to be pushed. The item contents are cloned (copied)
|
||||
* to the internal local copy, thus, this parameter may be changed as needed after the call
|
||||
* Note: if the queue is full, the function returned immediatelly with an error. This dunctionality can
|
||||
* be changed by letting the function block until space is freed up in the queue. When there is not
|
||||
* sufficient space in the queue, the item is dropped and the "dropped" counter is increased.
|
||||
*/
|
||||
int tsqueue_insert_push_item(tsqueue_st* q, tsqueue_item_st* item, int timeout_us, int override);
|
||||
|
||||
/*
|
||||
* Push a buffer to the queue
|
||||
* The same as before, but rather than an "item", a specific buffer, length and metadata are
|
||||
* cloned into the internal item local copy, and pushed to the queue
|
||||
*/
|
||||
int tsqueue_insert_push_buffer(tsqueue_st* q, uint8_t* buffer, int length, uint32_t metadata, int timeout_us, int override);
|
||||
|
||||
/*
|
||||
* Pop an item from the queue with timeout
|
||||
* item: the output of the function is a pointer to a single cell in the queue that has been
|
||||
* popped. This item pointer is not cloned to the user but rather passed by reference.
|
||||
* it is up to the user to create his own private copy of its contents as soon as he gets
|
||||
* the pointer.
|
||||
* Note: this function blocked until there is anything to pop. If the queue is empty, the queue will
|
||||
* hold the calling thread in a blocking condition.
|
||||
* For no timeout option pass 0 to timeout_ms
|
||||
*/
|
||||
int tsqueue_pop_item(tsqueue_st* q, tsqueue_item_st** item, int timeout_us);
|
||||
|
||||
/*
|
||||
* Return the current number of items in the queue
|
||||
*/
|
||||
int tsqueue_number_of_items(tsqueue_st* q);
|
||||
|
||||
/*
|
||||
* Clears the "dropped" counter value to zero.
|
||||
*/
|
||||
int tsqueue_reset_dropped_counter(tsqueue_st* q);
|
||||
|
||||
/*
|
||||
* Return the current count value of the dropped items - those we wanted to push but couldn't due
|
||||
* to insufficient capacity
|
||||
*/
|
||||
int tsqueue_get_number_of_dropped(tsqueue_st* q);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __TSQUEUE_H__
|
|
@ -1,2 +0,0 @@
|
|||
# build directories
|
||||
build
|
|
@ -1,25 +0,0 @@
|
|||
cmake_minimum_required(VERSION 3.15)
|
||||
project(cariboulite)
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
|
||||
# Bring the headers
|
||||
set(SUPER_DIR ${PROJECT_SOURCE_DIR}/..)
|
||||
include_directories(/.)
|
||||
include_directories(${SUPER_DIR})
|
||||
|
||||
# Source files
|
||||
set(SOURCES_LIB hat.c eeprom_utils.c)
|
||||
set(SOURCES ${SOURCES_LIB} test_hat.c)
|
||||
set(EXTERN_LIBS ${SUPER_DIR}/io_utils/build/libio_utils.a ${SUPER_DIR}/zf_log/build/libzf_log.a -lpthread)
|
||||
add_compile_options(-Wall -Wextra -Wmissing-braces)
|
||||
|
||||
#Generate the static library from the sources
|
||||
add_library(hat STATIC ${SOURCES_LIB})
|
||||
target_link_libraries(hat rt m pthread)
|
||||
|
||||
#add_executable(test_hat ${SOURCES})
|
||||
#target_link_libraries(test_hat rt pthread ${EXTERN_LIBS})
|
||||
|
||||
# Set the location for library installation -- i.e., /usr/lib in this case
|
||||
# not really necessary in this example. Use "sudo make install" to apply
|
||||
install(TARGETS hat DESTINATION /usr/lib)
|
|
@ -1,4 +0,0 @@
|
|||
[EEPROM Programming Tutorial](https://github.com/cariboulabs/cariboulite/tree/main/docs/flashing)
|
||||
|
||||
# License
|
||||
<a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/"><img alt="Creative Commons License" style="border-width:0" src="https://i.creativecommons.org/l/by-sa/4.0/88x31.png" /></a><br />This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">Creative Commons Attribution-ShareAlike 4.0 International License</a>.
|
|
@ -1,181 +0,0 @@
|
|||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "EEPROM_UTILS"
|
||||
#include "zf_log/zf_log.h"
|
||||
|
||||
#include "eeprom_utils.h"
|
||||
#include "io_utils/io_utils_fs.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <endian.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
//===========================================================
|
||||
int eeprom_init_device(eeprom_utils_st *ee)
|
||||
{
|
||||
switch (ee->eeprom_type)
|
||||
{
|
||||
case eeprom_type_24c64: strcpy(ee->eeprom_type_name, "24c64"); ee->eeprom_size = 8192; break;
|
||||
case eeprom_type_24c128: strcpy(ee->eeprom_type_name, "24c128"); ee->eeprom_size = 16384; break;
|
||||
case eeprom_type_24c256: strcpy(ee->eeprom_type_name, "24c256"); ee->eeprom_size = 32768; break;
|
||||
case eeprom_type_24c512: strcpy(ee->eeprom_type_name, "24c512"); ee->eeprom_size = 65536; break;
|
||||
case eeprom_type_24c1024: strcpy(ee->eeprom_type_name, "24c1024"); ee->eeprom_size = 131072; break;
|
||||
case eeprom_type_24c32:
|
||||
default: strcpy(ee->eeprom_type_name, "24c32"); ee->eeprom_size = 4096; break; // lowest denominator
|
||||
}
|
||||
|
||||
ee->bus = io_utils_i2cbus_exists();
|
||||
if (ee->bus >= 0)
|
||||
{
|
||||
ZF_LOGI("i2c-%d has been found successfully", ee->bus);
|
||||
}
|
||||
|
||||
// neither bus 0,9 were found in the dev dir -> we need to probe bus9
|
||||
if (ee->bus == -1)
|
||||
{
|
||||
if (io_utils_probe_gpio_i2c() == -1)
|
||||
{
|
||||
ZF_LOGE("Failed to probe i2c-9");
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ee->bus = 9;
|
||||
ZF_LOGI("i2c-9 has been probed successfully");
|
||||
}
|
||||
}
|
||||
|
||||
// probe the eeprom driver
|
||||
ZF_LOGI("trying to modprobe at24");
|
||||
char modprobe[] = "/usr/sbin/modprobe at24";
|
||||
char *argv[64];
|
||||
io_utils_parse_command(modprobe, argv);
|
||||
if (io_utils_execute_command(argv) != 0)
|
||||
{
|
||||
ZF_LOGE("MODPROBE of the eeprom 'at24' execution failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// the sys dir path
|
||||
char sys_dir_bus[128] = {0};
|
||||
char sys_dir_bus_addr[160] = {0};
|
||||
char sys_dir_bus_new_dev[160] = {0};
|
||||
sprintf(sys_dir_bus, "/sys/class/i2c-adapter/i2c-%d", ee->bus);
|
||||
sprintf(sys_dir_bus_addr, "%s/%d-00%x", sys_dir_bus, ee->bus, ee->i2c_address);
|
||||
sprintf(sys_dir_bus_new_dev, "%s/new_device", sys_dir_bus);
|
||||
|
||||
int dir = 0;
|
||||
int ee_exists = io_utils_file_exists(sys_dir_bus_addr, NULL, &dir, NULL, NULL);
|
||||
if (!ee_exists || !dir)
|
||||
{
|
||||
// create the device
|
||||
char dev_type[64] = {0};
|
||||
sprintf(dev_type, "%s 0x%x", ee->eeprom_type_name, ee->i2c_address);
|
||||
if ( io_utils_write_to_file(sys_dir_bus_new_dev, dev_type, strlen(dev_type) + 1) != 0)
|
||||
{
|
||||
ZF_LOGE("EEPROM on addr 0x%x probing failed, retrying...", ee->i2c_address);
|
||||
|
||||
if (io_utils_write_to_file(sys_dir_bus_new_dev, dev_type, strlen(dev_type) + 1) != 0)
|
||||
{
|
||||
ZF_LOGE("EEPROM on addr 0x%x probing failed", ee->i2c_address);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// recheck that the file exists now
|
||||
ee_exists = io_utils_file_exists(sys_dir_bus_addr, NULL, &dir, NULL, NULL);
|
||||
if (!ee_exists || !dir)
|
||||
{
|
||||
ZF_LOGE("EEPROM on addr 0x%x probing failed - file was not found", ee->i2c_address);
|
||||
return -1;
|
||||
}
|
||||
ZF_LOGI("EEPROM on addr 0x%x probing successful", ee->i2c_address);
|
||||
ee->initialized = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
int eeprom_close_device(eeprom_utils_st *ee)
|
||||
{
|
||||
int dir = 0;
|
||||
char sys_dir_bus[128] = {0};
|
||||
char sys_dir_bus_addr[160] = {0};
|
||||
char sys_dir_bus_del_dev[160] = {0};
|
||||
|
||||
if (ee->initialized == false)
|
||||
{
|
||||
ZF_LOGE("EEPROM device is not initialized");
|
||||
return -1;
|
||||
}
|
||||
|
||||
sprintf(sys_dir_bus, "/sys/class/i2c-adapter/i2c-%d", ee->bus);
|
||||
sprintf(sys_dir_bus_addr, "%s/%d-00%x", sys_dir_bus, ee->bus, ee->i2c_address);
|
||||
sprintf(sys_dir_bus_del_dev, "%s/delete_device", sys_dir_bus);
|
||||
|
||||
int ee_exists = io_utils_file_exists(sys_dir_bus_addr, NULL, &dir, NULL, NULL);
|
||||
if (ee_exists && dir)
|
||||
{
|
||||
char dev_type[64] = {0};
|
||||
sprintf(dev_type, "0x%x", ee->i2c_address);
|
||||
if (io_utils_write_to_file(sys_dir_bus_del_dev, dev_type, strlen(dev_type) + 1) != 0)
|
||||
{
|
||||
ZF_LOGE("EEPROM on addr 0x%x deletion failed on bus %d", ee->i2c_address, ee->bus);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
ZF_LOGI("EEPROM addr 0x%x on bus %d deletion was successful", ee->i2c_address, ee->bus);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
int eeprom_write(eeprom_utils_st *ee, char* buffer, int length)
|
||||
{
|
||||
char eeprom_fname[200] = {0};
|
||||
sprintf(eeprom_fname, "/sys/class/i2c-adapter/i2c-%d/%d-00%x/eeprom",
|
||||
ee->bus, ee->bus, ee->i2c_address);
|
||||
int ee_exists = io_utils_file_exists(eeprom_fname, NULL, NULL, NULL, NULL);
|
||||
if (!ee_exists)
|
||||
{
|
||||
ZF_LOGE("The eeprom driver for bus %d, adde 0x%x is not initialized", ee->bus, ee->i2c_address);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (length > ee->eeprom_size)
|
||||
{
|
||||
ZF_LOGW("EEPROM write size (length=%d) exceeds %d bytes, truncating", length, ee->eeprom_size);
|
||||
length = ee->eeprom_size;
|
||||
}
|
||||
return io_utils_write_to_file(eeprom_fname, buffer, length);
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
int eeprom_read(eeprom_utils_st *ee, char* buffer, int length)
|
||||
{
|
||||
char eeprom_fname[200] = {0};
|
||||
sprintf(eeprom_fname, "/sys/class/i2c-adapter/i2c-%d/%d-00%x/eeprom",
|
||||
ee->bus, ee->bus, ee->i2c_address);
|
||||
int ee_exists = io_utils_file_exists(eeprom_fname, NULL, NULL, NULL, NULL);
|
||||
if (!ee_exists)
|
||||
{
|
||||
ZF_LOGE("The eeprom driver for bus %d, adde 0x%x is not initialized", ee->bus, ee->i2c_address);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (length > ee->eeprom_size)
|
||||
{
|
||||
ZF_LOGW("EEPROM read size (length=%d) exceeds %d bytes, truncating", length, ee->eeprom_size);
|
||||
length = ee->eeprom_size;
|
||||
}
|
||||
|
||||
return io_utils_read_from_file(eeprom_fname, buffer, length);
|
||||
}
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
#ifndef __EEPROM_UTILS_H__
|
||||
#define __EEPROM_UTILS_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/* EEPROM types */
|
||||
typedef enum
|
||||
{
|
||||
eeprom_type_24c32 = 4096,
|
||||
eeprom_type_24c64 = 8192,
|
||||
eeprom_type_24c128 = 16384,
|
||||
eeprom_type_24c256 = 32768,
|
||||
eeprom_type_24c512 = 65536,
|
||||
eeprom_type_24c1024 = 131072,
|
||||
} eeprom_type_en;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t i2c_address;
|
||||
eeprom_type_en eeprom_type;
|
||||
char eeprom_type_name[32];
|
||||
|
||||
int bus;
|
||||
int eeprom_size;
|
||||
int initialized;
|
||||
} eeprom_utils_st;
|
||||
|
||||
int eeprom_init_device(eeprom_utils_st *ee);
|
||||
int eeprom_close_device(eeprom_utils_st *ee);
|
||||
int eeprom_write(eeprom_utils_st *ee, char* buffer, int length);
|
||||
int eeprom_read(eeprom_utils_st *ee, char* buffer, int length);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __EEPROM_UTILS_H__
|
|
@ -1,721 +0,0 @@
|
|||
#define ZF_LOG_LEVEL ZF_LOG_VERBOSE
|
||||
#define ZF_LOG_DEF_SRCLOC ZF_LOG_SRCLOC_LONG
|
||||
#define ZF_LOG_TAG "HAT"
|
||||
#include "zf_log/zf_log.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdbool.h>
|
||||
#include <endian.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/types.h>
|
||||
#include "io_utils/io_utils_fs.h"
|
||||
#include "hat.h"
|
||||
|
||||
|
||||
//===========================================================
|
||||
int serial_from_uuid(char* uuid, uint32_t *serial)
|
||||
{
|
||||
uint32_t data0 = 0, data4 = 0;
|
||||
uint16_t data1 = 0, data2 = 0, data3 = 0, data5 = 0;
|
||||
uint32_t ser1, ser2, ser3, ser4;
|
||||
if (sscanf(uuid, "%08x-%04hx-%04hx-%04hx-%08x%04hx",
|
||||
&data0, &data1, &data2,
|
||||
&data3, &data4, &data5) != 6)
|
||||
{
|
||||
ZF_LOGE("the uuid '%s' is not valid", uuid);
|
||||
return -1;
|
||||
}
|
||||
ser1 = data5;
|
||||
ser2 = (data4 & 0xFFFF) | (data3 << 16);
|
||||
ser3 = (data2 & 0xFFFF) | (data1 << 16);
|
||||
ser4 = data0;
|
||||
if (serial) *serial = ser1 ^ ser2 ^ ser3 ^ ser4;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
static uint16_t getcrc(char* data, unsigned int size)
|
||||
{
|
||||
uint16_t out = 0;
|
||||
int bits_read = 0, bit_flag;
|
||||
|
||||
/* Sanity check: */
|
||||
if((data == NULL) || size == 0)
|
||||
return 0;
|
||||
|
||||
while(size > 0)
|
||||
{
|
||||
bit_flag = out >> 15;
|
||||
|
||||
/* Get next bit: */
|
||||
out <<= 1;
|
||||
// item a) work from the least significant bits
|
||||
out |= (*data >> bits_read) & 1;
|
||||
|
||||
/* Increment bit counter: */
|
||||
bits_read++;
|
||||
if(bits_read > 7)
|
||||
{
|
||||
bits_read = 0;
|
||||
data++;
|
||||
size--;
|
||||
}
|
||||
|
||||
/* Cycle check: */
|
||||
if(bit_flag)
|
||||
out ^= CRC16_POLY;
|
||||
}
|
||||
|
||||
// item b) "push out" the last 16 bits
|
||||
int i;
|
||||
for (i = 0; i < 16; ++i) {
|
||||
bit_flag = out >> 15;
|
||||
out <<= 1;
|
||||
if(bit_flag)
|
||||
out ^= CRC16_POLY;
|
||||
}
|
||||
|
||||
// item c) reverse the bits
|
||||
uint16_t crc = 0;
|
||||
i = 0x8000;
|
||||
int j = 0x0001;
|
||||
for (; i != 0; i >>=1, j <<= 1) {
|
||||
if (i & out) crc |= j;
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
static void hat_print_header(struct header_t *header)
|
||||
{
|
||||
ZF_LOGD("# Header: signature=0x%08x", header->signature);
|
||||
ZF_LOGD("# Header: format version=0x%02x", header->ver);
|
||||
ZF_LOGD("# Header: reserved=%u", header->res);
|
||||
ZF_LOGD("# Header: numatoms=%u", header->numatoms);
|
||||
ZF_LOGD("# Header: eeplen=%u", header->eeplen);
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
static void hat_print_vendor(struct vendor_info_t * vinf)
|
||||
{
|
||||
ZF_LOGD("Vendor info: product_uuid %08x-%04x-%04x-%04x-%04x%08x",
|
||||
vinf->serial_4,
|
||||
vinf->serial_3>>16,
|
||||
vinf->serial_3 & 0xffff,
|
||||
vinf->serial_2>>16,
|
||||
vinf->serial_2 & 0xffff,
|
||||
vinf->serial_1);
|
||||
|
||||
ZF_LOGD("Vendor info: raw serial numbers %08x %08x %08x %08x",
|
||||
vinf->serial_4,
|
||||
vinf->serial_3,
|
||||
vinf->serial_2,
|
||||
vinf->serial_1);
|
||||
ZF_LOGD("Vendor info: product_id 0x%04x", vinf->pid);
|
||||
ZF_LOGD("Vendor info: product_ver 0x%04x", vinf->pver);
|
||||
ZF_LOGD("Vendor info: vendor \"%s\" # length=%u", vinf->vstr, vinf->vslen);
|
||||
ZF_LOGD("Vendor info: product \"%s\" # length=%u", vinf->pstr, vinf->pslen);
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
static void hat_print_gpio(struct gpio_map_t *gpiomap)
|
||||
{
|
||||
ZF_LOGD("GPIO map info: gpio_drive %d", gpiomap->flags & 15); //1111
|
||||
ZF_LOGD("GPIO map info: gpio_slew %d", (gpiomap->flags & 48)>>4); //110000
|
||||
ZF_LOGD("GPIO map info: gpio_hysteresis %d", (gpiomap->flags & 192)>>6); //11000000
|
||||
ZF_LOGD("GPIO map info: back_power %d", gpiomap->power);
|
||||
|
||||
for (int j = 0; j<28; j++)
|
||||
{
|
||||
if (gpiomap->pins[j] & (1<<7))
|
||||
{
|
||||
//board uses this pin
|
||||
char *pull_str = "INVALID";
|
||||
switch ((gpiomap->pins[j] & 96)>>5) { //1100000
|
||||
case 0: pull_str = "PULL DEFAULT";
|
||||
break;
|
||||
case 1: pull_str = "PULL UP";
|
||||
break;
|
||||
case 2: pull_str = "PULL DOWN";
|
||||
break;
|
||||
case 3: pull_str = "PULL NONE";
|
||||
break;
|
||||
}
|
||||
|
||||
char *func_str = "INVALID";
|
||||
switch ((gpiomap->pins[j] & 7)) { //111
|
||||
case 0: func_str = "INPUT";
|
||||
break;
|
||||
case 1: func_str = "OUTPUT";
|
||||
break;
|
||||
case 4: func_str = "ALT0";
|
||||
break;
|
||||
case 5: func_str = "ALT1";
|
||||
break;
|
||||
case 6: func_str = "ALT2";
|
||||
break;
|
||||
case 7: func_str = "ALT3";
|
||||
break;
|
||||
case 3: func_str = "ALT4";
|
||||
break;
|
||||
case 2: func_str = "ALT5";
|
||||
break;
|
||||
}
|
||||
|
||||
ZF_LOGD("# GPIO map info: setgpio %d %s %s", j, func_str, pull_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
static void hat_print_dt_data(struct dt_data_t *data)
|
||||
{
|
||||
ZF_LOGD("# Device Tree info: length = %d", data->dt_data_size);
|
||||
}
|
||||
|
||||
|
||||
//===========================================================
|
||||
static int hat_valid(hat_st *hat)
|
||||
{
|
||||
if (!hat->initialized)
|
||||
{
|
||||
ZF_LOGE("eeprom driver is not initialized");
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t *location = (uint8_t*)hat->read_buffer;
|
||||
uint32_t offset = 0;
|
||||
|
||||
// check the header
|
||||
struct header_t* header = (struct header_t*)location;
|
||||
if (header->signature != HEADER_SIGN || header->ver != FORMAT_VERSION)
|
||||
{
|
||||
// signature: 0x52, 0x2D, 0x50, 0x69 ("R-Pi" in ASCII)
|
||||
// EEPROM data format version (0x00 reserved, 0x01 = first version)
|
||||
ZF_LOGD("Signature (0x%08X) / version (0x%02X) not valid", header->signature, header->ver);
|
||||
return 0; // not valid
|
||||
}
|
||||
|
||||
if (header->res != 0)
|
||||
{
|
||||
ZF_LOGD("Reserved field not zero (0x%08X)", header->res);
|
||||
return 0; // not valid
|
||||
}
|
||||
|
||||
if (header->numatoms < 2)
|
||||
{
|
||||
ZF_LOGD("Number of atoms smaller than 3 (%d)", header->numatoms);
|
||||
return 0; // not valid
|
||||
}
|
||||
|
||||
if (header->eeplen > (uint32_t)(hat->read_buffer_size))
|
||||
{
|
||||
ZF_LOGD("The declared data-size larger than eeprom size (%d > %d)",
|
||||
header->eeplen, hat->read_buffer_size);
|
||||
return 0; // not valid
|
||||
}
|
||||
|
||||
// Now check every atom and check its validity
|
||||
// we won't dive deeper in the atoms as the crc16 should be sufficiently
|
||||
// informative on the validity in addition to all the constants etc.
|
||||
int i;
|
||||
location += sizeof(struct header_t);
|
||||
offset += sizeof(struct header_t);
|
||||
for (i = 0; i<header->numatoms; i++)
|
||||
{
|
||||
struct atom_t *atom = (struct atom_t *)location;
|
||||
if (atom->type != ATOM_VENDOR_TYPE &&
|
||||
atom->type != ATOM_GPIO_TYPE &&
|
||||
atom->type != ATOM_DT_TYPE &&
|
||||
atom->type != ATOM_CUSTOM_TYPE)
|
||||
{
|
||||
ZF_LOGD("Found an invalid atom type (%d @ #%d)", atom->type, i);
|
||||
return 0; // not valid
|
||||
}
|
||||
|
||||
if (atom->count != i)
|
||||
{
|
||||
ZF_LOGD("Atom #%d count inconcistent (%d)", i, atom->count);
|
||||
return 0; // not valid
|
||||
}
|
||||
|
||||
if ((offset + ATOM_TOTAL_SIZE(atom)) > (uint32_t)(hat->read_buffer_size))
|
||||
{
|
||||
ZF_LOGD("Atom #%d data length + crc16 don't fit into eeprom", i);
|
||||
return 0; // not valid
|
||||
}
|
||||
|
||||
// calculate crc
|
||||
uint16_t calc_crc = getcrc((char*)atom, ATOM_DATA_SIZE(atom));
|
||||
uint16_t actual_crc = ATOM_CRC(atom);
|
||||
if (actual_crc != calc_crc)
|
||||
{
|
||||
ZF_LOGD("Atom #%d calc_crc (0x%04X) doesn't match the actual_crc (0x%04X)",
|
||||
i, calc_crc, actual_crc);
|
||||
return 0; // not valid
|
||||
}
|
||||
|
||||
location += ATOM_TOTAL_SIZE(atom);
|
||||
offset += ATOM_TOTAL_SIZE(atom);
|
||||
}
|
||||
|
||||
if (header->eeplen != offset)
|
||||
{
|
||||
ZF_LOGD("The eeprom header total length doesn't match contents calculated size (%d <=> %d)",
|
||||
header->eeplen, offset);
|
||||
return 0; // not valid
|
||||
}
|
||||
|
||||
return 1; // valid
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
static int hat_contents_parse(hat_st *hat)
|
||||
{
|
||||
uint8_t *location = NULL;
|
||||
if (!hat->initialized)
|
||||
{
|
||||
ZF_LOGE("eeprom driver is not initialized");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ZF_LOGD("Reading eeprom configuration (%d bytes)...", hat->read_buffer_size);
|
||||
if (eeprom_read(&hat->dev, hat->read_buffer, hat->read_buffer_size) < 0)
|
||||
{
|
||||
ZF_LOGE("Reading from eeprom failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// check the eeprom data's validity
|
||||
if ( !hat_valid(hat) )
|
||||
{
|
||||
ZF_LOGE("EEPROM data is not valid. Try reconfiguring it.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
location = (uint8_t*)hat->read_buffer;
|
||||
|
||||
// Header
|
||||
memcpy(&hat->header, location, sizeof(hat->header));
|
||||
location += sizeof(hat->header);
|
||||
|
||||
// Atoms
|
||||
for (int i = 0; i < hat->header.numatoms; i++)
|
||||
{
|
||||
struct atom_t *atom = (struct atom_t *)location;
|
||||
uint8_t *atom_data = location + ATOM_HEADER_SIZE;
|
||||
|
||||
// Analyze he atom internal infomration
|
||||
switch (atom->type)
|
||||
{
|
||||
//-------------------------------------------------------------
|
||||
case ATOM_VENDOR_TYPE:
|
||||
{
|
||||
uint8_t *it = atom_data;
|
||||
memcpy(&hat->vinf, it, VENDOR_STATIC_SIZE); it += VENDOR_STATIC_SIZE;
|
||||
memcpy(&hat->vinf.vstr, it, hat->vinf.vslen); it += hat->vinf.vslen;
|
||||
memcpy(&hat->vinf.pstr, it, hat->vinf.pslen); it += hat->vinf.pslen;
|
||||
hat->vinf.vstr[hat->vinf.vslen] = 0;
|
||||
hat->vinf.pstr[hat->vinf.pslen] = 0;
|
||||
} break;
|
||||
|
||||
//-------------------------------------------------------------
|
||||
case ATOM_GPIO_TYPE:
|
||||
{
|
||||
memcpy(&hat->gpiomap, atom_data, GPIO_MAP_SIZE);
|
||||
} break;
|
||||
|
||||
//-------------------------------------------------------------
|
||||
case ATOM_DT_TYPE:
|
||||
{
|
||||
ZF_LOGD("Atom datalength = %d", atom->dlen - 2); // substruct the crc16 size from the dlen
|
||||
hat->dt_data.dt_data = (char*)malloc(atom->dlen - 2);
|
||||
if (hat->dt_data.dt_data == NULL)
|
||||
{
|
||||
ZF_LOGE("Failed allocating dt data.");
|
||||
return -1;
|
||||
}
|
||||
hat->dt_data.dt_data_size = atom->dlen - 2;
|
||||
memcpy(hat->dt_data.dt_data, atom_data, hat->dt_data.dt_data_size);
|
||||
} break;
|
||||
|
||||
//-------------------------------------------------------------
|
||||
default:
|
||||
ZF_LOGE("Error: unrecognised atom type");
|
||||
break;
|
||||
}
|
||||
|
||||
location += ATOM_TOTAL_SIZE(atom);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
int hat_fill_in(hat_st *hat)
|
||||
{
|
||||
struct atom_t *atom = NULL;
|
||||
uint8_t *location = (uint8_t *)hat->write_buffer;
|
||||
struct header_t* header = (struct header_t*)hat->write_buffer;
|
||||
|
||||
// Header generation
|
||||
// -------------------------------------------------------
|
||||
header->signature = HEADER_SIGN;
|
||||
header->ver = FORMAT_VERSION;
|
||||
header->res = 0;
|
||||
header->numatoms = 0;
|
||||
header->eeplen = sizeof(struct header_t);
|
||||
|
||||
// Vendor information generation
|
||||
// -------------------------------------------------------
|
||||
location += header->eeplen;
|
||||
atom = (struct atom_t*)location;
|
||||
struct vendor_info_t* vinf = (struct vendor_info_t*)(location + ATOM_HEADER_SIZE);
|
||||
|
||||
vinf->pid = hat->product_id;
|
||||
vinf->pver = hat->product_version;
|
||||
vinf->vslen = strlen(hat->vendor_name);
|
||||
vinf->pslen = strlen(hat->product_name);
|
||||
strcpy(VENDOR_VSTR_POINT(vinf), hat->vendor_name);
|
||||
strcpy(VENDOR_PSTR_POINT(vinf), hat->product_name);
|
||||
|
||||
// read 128 random bits from /dev/urandom
|
||||
int random_file = open("/dev/urandom", O_RDONLY);
|
||||
void* temp_serial_loc = (void*)&vinf->serial_1;
|
||||
ssize_t result = read(random_file, temp_serial_loc, 16);
|
||||
close(random_file);
|
||||
|
||||
if (result <= 0)
|
||||
{
|
||||
printf("Unable to read from /dev/urandom to set up UUID");
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
//put in the version
|
||||
vinf->serial_3 = (vinf->serial_3 & 0xffff0fff) | 0x00004000;
|
||||
|
||||
//put in the variant
|
||||
vinf->serial_2 = (vinf->serial_2 & 0x3fffffff) | 0x80000000;
|
||||
|
||||
printf("Gen UUID=%08x-%04x-%04x-%04x-%04x%08x\n", vinf->serial_4,
|
||||
vinf->serial_3>>16,
|
||||
vinf->serial_3 & 0xffff,
|
||||
vinf->serial_2>>16,
|
||||
vinf->serial_2 & 0xffff,
|
||||
vinf->serial_1);
|
||||
sprintf(hat->generated_uuid, "%08x-%04x-%04x-%04x-%04x%08x", vinf->serial_4,
|
||||
vinf->serial_3>>16,
|
||||
vinf->serial_3 & 0xffff,
|
||||
vinf->serial_2>>16,
|
||||
vinf->serial_2 & 0xffff,
|
||||
vinf->serial_1);
|
||||
serial_from_uuid(hat->generated_uuid, &hat->generated_serial);
|
||||
}
|
||||
|
||||
atom->type = ATOM_VENDOR_TYPE;
|
||||
atom->count = header->numatoms;
|
||||
atom->dlen = VENDOR_INFO_COMPACT_SIZE(vinf) + 2;
|
||||
ATOM_CRC(atom) = getcrc((char*)atom, ATOM_DATA_SIZE(atom));
|
||||
header->eeplen += ATOM_TOTAL_SIZE(atom);
|
||||
header->numatoms += 1;
|
||||
|
||||
// GPIO map information
|
||||
// -------------------------------------------------------
|
||||
location += ATOM_TOTAL_SIZE(atom);
|
||||
atom = (struct atom_t*)location;
|
||||
atom->type = ATOM_GPIO_TYPE;
|
||||
atom->count = header->numatoms;
|
||||
atom->dlen = GPIO_MAP_SIZE + 2;
|
||||
struct gpio_map_t* gpio = (struct gpio_map_t*)(location+ATOM_HEADER_SIZE);
|
||||
gpio->flags = 0; // drive, slew, hysteresis => 0=leave at default
|
||||
gpio->power = 0; // 0 = no back power
|
||||
|
||||
// MAPPING: (func,pull,used)
|
||||
// [2:0] func_sel GPIO function as per FSEL GPIO register field in BCM2835 datasheet
|
||||
// [4:3] reserved set to 0
|
||||
// [6:5] pulltype 0=leave at default setting, 1=pullup, 2=pulldown, 3=no pull
|
||||
// [ 7] is_used 1=board uses this pin, 0=not connected and therefore not used
|
||||
|
||||
gpio->pins[2] = GPIO_MAP_BITS(5,2,0); // SMI SA3
|
||||
gpio->pins[3] = GPIO_MAP_BITS(5,2,0); // SMI SA2
|
||||
gpio->pins[4] = GPIO_MAP_BITS(1,0,1); // FPGA SOFT RESET
|
||||
gpio->pins[5] = GPIO_MAP_BITS(1,0,1); // MXR_RESET
|
||||
gpio->pins[6] = GPIO_MAP_BITS(5,2,1); // SMI SOE_SE
|
||||
gpio->pins[7] = GPIO_MAP_BITS(5,2,1); // SMI SWE_SRW
|
||||
gpio->pins[8] = GPIO_MAP_BITS(5,0,1); // SMI SD0
|
||||
gpio->pins[9] = GPIO_MAP_BITS(5,0,1); // SMI SD1
|
||||
gpio->pins[10] = GPIO_MAP_BITS(5,0,1); // SMI SD2
|
||||
gpio->pins[11] = GPIO_MAP_BITS(5,0,1); // SMI SD3
|
||||
gpio->pins[12] = GPIO_MAP_BITS(5,0,1); // SMI SD4
|
||||
gpio->pins[13] = GPIO_MAP_BITS(5,0,1); // SMI SD5
|
||||
gpio->pins[14] = GPIO_MAP_BITS(5,0,1); // SMI SD6
|
||||
gpio->pins[15] = GPIO_MAP_BITS(5,0,1); // SMI SD7
|
||||
gpio->pins[16] = GPIO_MAP_BITS(0,0,1); // SPI1 CS #2 - MIXER
|
||||
gpio->pins[17] = GPIO_MAP_BITS(0,0,1); // SPI1 CS #1 - MODEM
|
||||
gpio->pins[18] = GPIO_MAP_BITS(0,0,1); // SPI1 CS #0 - FPGA
|
||||
gpio->pins[19] = GPIO_MAP_BITS(0,0,1); // SPI1 MISO
|
||||
gpio->pins[20] = GPIO_MAP_BITS(0,0,1); // SPI1 MOSI
|
||||
gpio->pins[21] = GPIO_MAP_BITS(0,0,1); // SPI1 SCK
|
||||
gpio->pins[22] = GPIO_MAP_BITS(0,1,1); // MODEM IRQ
|
||||
gpio->pins[23] = GPIO_MAP_BITS(1,0,1); // MODEM RESET
|
||||
gpio->pins[24] = GPIO_MAP_BITS(5,0,1); // SMI READ_REQ
|
||||
gpio->pins[25] = GPIO_MAP_BITS(5,0,1); // SMI WRITE_REQ
|
||||
gpio->pins[26] = GPIO_MAP_BITS(1,0,1); // FPGA RESET
|
||||
gpio->pins[27] = GPIO_MAP_BITS(0,0,1); // FPGA CDONE
|
||||
ATOM_CRC(atom) = getcrc((char*)atom, ATOM_DATA_SIZE(atom));
|
||||
|
||||
header->eeplen += ATOM_TOTAL_SIZE(atom);
|
||||
header->numatoms += 1;
|
||||
|
||||
// Device Tree information
|
||||
// -------------------------------------------------------
|
||||
location += ATOM_TOTAL_SIZE(atom);
|
||||
atom = (struct atom_t*)location;
|
||||
atom->type = ATOM_DT_TYPE;
|
||||
atom->count = header->numatoms;
|
||||
atom->dlen = hat->device_tree_buffer_size + 2;
|
||||
uint8_t *dt_data = (uint8_t *)(location+ATOM_HEADER_SIZE);
|
||||
memcpy(dt_data, hat->device_tree_buffer, hat->device_tree_buffer_size);
|
||||
ATOM_CRC(atom) = getcrc((char*)atom, ATOM_DATA_SIZE(atom));
|
||||
|
||||
header->eeplen += ATOM_TOTAL_SIZE(atom);
|
||||
header->numatoms += 1;
|
||||
|
||||
hat->write_buffer_used_size = header->eeplen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
int hat_init(hat_st *hat)
|
||||
{
|
||||
ZF_LOGD("Initializing eeprom driver");
|
||||
if (eeprom_init_device(&hat->dev) != 0)
|
||||
{
|
||||
ZF_LOGE("Initializing hat driver failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
hat->read_buffer = NULL;
|
||||
hat->write_buffer = NULL;
|
||||
hat->read_buffer_size = hat->dev.eeprom_size;
|
||||
hat->read_buffer = (char *)malloc(hat->read_buffer_size);
|
||||
if (hat->read_buffer == NULL)
|
||||
{
|
||||
ZF_LOGE("hat read buffer allocation failed");
|
||||
eeprom_close_device(&hat->dev);
|
||||
return -1;
|
||||
}
|
||||
|
||||
hat->write_buffer_size = hat->dev.eeprom_size;
|
||||
hat->write_buffer = (char *)malloc(hat->write_buffer_size);
|
||||
if (hat->write_buffer == NULL)
|
||||
{
|
||||
ZF_LOGE("hat write buffer allocation failed");
|
||||
eeprom_close_device(&hat->dev);
|
||||
return -1;
|
||||
}
|
||||
hat->write_buffer_used_size = 0;
|
||||
hat->initialized = true;
|
||||
|
||||
// check if the eeprom is initialized (of contains FFFF garbage)
|
||||
hat->eeprom_initialized = false;
|
||||
if (eeprom_read(&hat->dev, hat->read_buffer, hat->read_buffer_size) < 0)
|
||||
{
|
||||
ZF_LOGE("Reading from eeprom failed");
|
||||
return -1;
|
||||
}
|
||||
hat->eeprom_initialized = hat_valid(hat);
|
||||
hat_contents_parse(hat);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
int hat_close(hat_st *hat)
|
||||
{
|
||||
ZF_LOGD("closing hat driver");
|
||||
if (!hat->initialized)
|
||||
{
|
||||
ZF_LOGE("hat is not initialized");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (hat->read_buffer != NULL) free(hat->read_buffer);
|
||||
if (hat->write_buffer != NULL) free(hat->write_buffer);
|
||||
hat->read_buffer_size = 0;
|
||||
hat->write_buffer_size = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
int hat_generate_write_config(hat_st *hat, bool overwrite)
|
||||
{
|
||||
if (!hat->eeprom_initialized || overwrite)
|
||||
{
|
||||
ZF_LOGD("Filling in HAT information");
|
||||
hat_fill_in(hat);
|
||||
ZF_LOGD("Writing into HAT");
|
||||
eeprom_write(&hat->dev, hat->write_buffer, hat->write_buffer_used_size);
|
||||
ZF_LOGD("Writing into HAT - Done");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
sprintf(hat->generated_uuid, "%08x-%04x-%04x-%04x-%04x%08x", hat->vinf.serial_4,
|
||||
hat->vinf.serial_3>>16,
|
||||
hat->vinf.serial_3 & 0xffff,
|
||||
hat->vinf.serial_2>>16,
|
||||
hat->vinf.serial_2 & 0xffff,
|
||||
hat->vinf.serial_1);
|
||||
serial_from_uuid(hat->generated_uuid, &hat->generated_serial);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
int hat_print(hat_st *hat)
|
||||
{
|
||||
if (!hat->eeprom_initialized)
|
||||
{
|
||||
if (hat_contents_parse(hat) != 0)
|
||||
{
|
||||
ZF_LOGE("Parsing EEPROM data failed - try reconfiguring");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
hat_print_header(&hat->header);
|
||||
hat_print_vendor(&hat->vinf);
|
||||
hat_print_gpio(&hat->gpiomap);
|
||||
hat_print_dt_data(&hat->dt_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
// If the board is not detected, try detecting it outside:
|
||||
// go directly to the eeprom configuration application
|
||||
// prompt the user
|
||||
// configure and tell the user he needs to reboot his system
|
||||
int hat_detect_board(hat_board_info_st *info)
|
||||
{
|
||||
int exists = 0;
|
||||
int size, dir, file, dev;
|
||||
|
||||
// check if a hat is attached anyway..
|
||||
char hat_dir_path[] = "/proc/device-tree/hat";
|
||||
exists = io_utils_file_exists(hat_dir_path, &size, &dir, &file, &dev);
|
||||
if (!exists || !dir)
|
||||
{
|
||||
ZF_LOGI("This board is not configured yet as a hat.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
io_utils_read_string_from_file(hat_dir_path, "name", info->category_name, sizeof(info->category_name));
|
||||
io_utils_read_string_from_file(hat_dir_path, "product", info->product_name, sizeof(info->product_name));
|
||||
io_utils_read_string_from_file(hat_dir_path, "product_id", info->product_id, sizeof(info->product_id));
|
||||
io_utils_read_string_from_file(hat_dir_path, "product_ver", info->product_version, sizeof(info->product_version));
|
||||
io_utils_read_string_from_file(hat_dir_path, "uuid", info->product_uuid, sizeof(info->product_uuid));
|
||||
io_utils_read_string_from_file(hat_dir_path, "vendor", info->product_vendor, sizeof(info->product_vendor));
|
||||
|
||||
// numeric version
|
||||
if (info->product_version[0] == '0' && (info->product_version[1] == 'x' ||
|
||||
info->product_version[1] == 'X'))
|
||||
sscanf(info->product_version, "0x%08x", &info->numeric_version);
|
||||
else
|
||||
sscanf(info->product_version, "%08x", &info->numeric_version);
|
||||
|
||||
// numeric productid
|
||||
if (info->product_id[0] == '0' && (info->product_id[1] == 'x' || info->product_id[1] == 'X'))
|
||||
sscanf(info->product_id, "0x%08x", &info->numeric_product_id);
|
||||
else
|
||||
sscanf(info->product_id, "%08x", &info->numeric_product_id);
|
||||
|
||||
// serial number
|
||||
if (serial_from_uuid(info->product_uuid, &info->numeric_serial_number) != 0)
|
||||
{
|
||||
// should never happen
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
int hat_detect_from_eeprom(hat_board_info_st *info)
|
||||
{
|
||||
hat_st hat =
|
||||
{
|
||||
.dev =
|
||||
{
|
||||
.i2c_address = 0x50, // the i2c address of the eeprom chip
|
||||
.eeprom_type = eeprom_type_24c32,
|
||||
},
|
||||
};
|
||||
|
||||
if (hat_init(&hat) != 0 || (info == NULL))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!hat.eeprom_initialized)
|
||||
{
|
||||
hat_close(&hat);
|
||||
return 0;
|
||||
}
|
||||
|
||||
sprintf(info->category_name, "hat");
|
||||
memcpy(info->product_name, VENDOR_PSTR_POINT(&hat.vinf), hat.vinf.pslen);
|
||||
info->product_name[hat.vinf.pslen] = 0;
|
||||
sprintf(info->product_id, "%d", hat.vinf.pid);
|
||||
sprintf(info->product_version, "%d", hat.vinf.pver);
|
||||
memcpy(info->product_vendor, VENDOR_VSTR_POINT(&hat.vinf), hat.vinf.vslen);
|
||||
info->product_vendor[hat.vinf.vslen] = 0;
|
||||
|
||||
sprintf(info->product_uuid, "%08x-%04x-%04x-%04x-%04x%08x", hat.vinf.serial_4,
|
||||
hat.vinf.serial_3>>16,
|
||||
hat.vinf.serial_3 & 0xffff,
|
||||
hat.vinf.serial_2>>16,
|
||||
hat.vinf.serial_2 & 0xffff,
|
||||
hat.vinf.serial_1);
|
||||
|
||||
info->numeric_version = hat.vinf.pver;
|
||||
info->numeric_product_id = hat.vinf.pid;
|
||||
|
||||
serial_from_uuid(info->product_uuid, &info->numeric_serial_number);
|
||||
|
||||
hat_close(&hat);
|
||||
return 1;
|
||||
}
|
||||
|
||||
//===========================================================
|
||||
void hat_print_board_info(hat_board_info_st *info, bool log)
|
||||
{
|
||||
if (log)
|
||||
{
|
||||
ZF_LOGD("# Board Info - Category name: %s", info->category_name);
|
||||
ZF_LOGD("# Board Info - Product name: %s", info->product_name);
|
||||
ZF_LOGD("# Board Info - Product ID: %s, Numeric: %d", info->product_id, info->numeric_product_id);
|
||||
ZF_LOGD("# Board Info - Product Version: %s, Numeric: %d", info->product_version, info->numeric_version);
|
||||
ZF_LOGD("# Board Info - Product UUID: %s, Numeric serial: 0x%08X", info->product_uuid, info->numeric_serial_number);
|
||||
ZF_LOGD("# Board Info - Vendor: %s", info->product_vendor);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf(" Category name: %s\n", info->category_name);
|
||||
printf(" Product name: %s\n", info->product_name);
|
||||
printf(" Product ID: %s, Numeric: %d\n", info->product_id, info->numeric_product_id);
|
||||
printf(" Product Version: %s, Numeric: %d\n", info->product_version, info->numeric_version);
|
||||
printf(" Product UUID: %s, Numeric serial: 0x%08X\n", info->product_uuid, info->numeric_serial_number);
|
||||
printf(" Vendor: %s\n", info->product_vendor);
|
||||
}
|
||||
}
|
|
@ -1,173 +0,0 @@
|
|||
#ifndef __HAT_H__
|
||||
#define __HAT_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "eeprom_utils.h"
|
||||
|
||||
|
||||
/* Header type */
|
||||
#define FORMAT_VERSION 0x01
|
||||
#define MAX_STRLEN 256
|
||||
|
||||
// Signature is "R-Pi" in ASCII. It is required to reversed (little endian) on disk.
|
||||
#define HEADER_SIGN be32toh((((char)'R' << 24) | ((char)'-' << 16) | ((char)'P' << 8) | ((char)'i')))
|
||||
|
||||
/* Atom type */
|
||||
#define ATOM_INVALID_TYPE 0x0000
|
||||
#define ATOM_VENDOR_TYPE 0x0001
|
||||
#define ATOM_GPIO_TYPE 0x0002
|
||||
#define ATOM_DT_TYPE 0x0003
|
||||
#define ATOM_CUSTOM_TYPE 0x0004
|
||||
#define ATOM_HINVALID_TYPE 0xffff
|
||||
#define ATOM_VENDOR_NUM 0x0000
|
||||
#define ATOM_GPIO_NUM 0x0001
|
||||
#define ATOM_DT_NUM 0x0002
|
||||
#define CRC16_POLY 0x8005
|
||||
|
||||
/* GPIO type */
|
||||
#define GPIO_MIN 2
|
||||
#define GPIO_COUNT 28
|
||||
|
||||
/* EEPROM header structure */
|
||||
struct header_t {
|
||||
uint32_t signature;
|
||||
unsigned char ver;
|
||||
unsigned char res;
|
||||
uint16_t numatoms;
|
||||
uint32_t eeplen;
|
||||
};
|
||||
|
||||
#define HEADER_SIZE ( sizeof(struct header_t) )
|
||||
|
||||
/* Atom structure */
|
||||
struct atom_t
|
||||
{
|
||||
uint16_t type;
|
||||
uint16_t count;
|
||||
uint32_t dlen;
|
||||
char* data;
|
||||
uint16_t crc16;
|
||||
};
|
||||
|
||||
#define ATOM_HEADER_SIZE ( 8 )
|
||||
#define ATOM_CRC_SIZE ( 2 )
|
||||
#define ATOM_DATA_SIZE(a) ( ATOM_HEADER_SIZE + ((a)->dlen) - 2 ) // at the dlen includes the CRC16 size
|
||||
#define ATOM_CRC(a) ( *(uint16_t*)( ((uint8_t*)(a)) + ATOM_DATA_SIZE(a) ) )
|
||||
#define ATOM_TOTAL_SIZE(a) ( ATOM_DATA_SIZE(a) + ATOM_CRC_SIZE)
|
||||
|
||||
/* Vendor info atom data */
|
||||
struct vendor_info_t
|
||||
{
|
||||
uint32_t serial_1; //least significant
|
||||
uint32_t serial_2;
|
||||
uint32_t serial_3;
|
||||
uint32_t serial_4; //most significant
|
||||
uint16_t pid;
|
||||
uint16_t pver;
|
||||
unsigned char vslen;
|
||||
unsigned char pslen;
|
||||
char vstr[MAX_STRLEN + 1];
|
||||
char pstr[MAX_STRLEN + 1];
|
||||
};
|
||||
|
||||
#define VENDOR_STATIC_SIZE ( 4*sizeof(uint32_t) + 2*sizeof(uint16_t) + 2*sizeof(char) )
|
||||
#define VENDOR_VSTR_POINT(v) ( (char*)(v) + VENDOR_STATIC_SIZE )
|
||||
#define VENDOR_PSTR_POINT(v) ( (char*)(v) + VENDOR_STATIC_SIZE + (v)->vslen )
|
||||
#define VENDOR_INFO_COMPACT_SIZE(v) ( VENDOR_STATIC_SIZE + (v)->vslen + (v)->pslen )
|
||||
|
||||
/* GPIO map atom data */
|
||||
struct gpio_map_t
|
||||
{
|
||||
unsigned char flags;
|
||||
unsigned char power;
|
||||
unsigned char pins[GPIO_COUNT];
|
||||
};
|
||||
#define GPIO_MAP_SIZE (sizeof(struct gpio_map_t))
|
||||
|
||||
// [2:0] func_sel GPIO function as per FSEL GPIO register field in BCM2835 datasheet
|
||||
// [4:3] reserved set to 0
|
||||
// [6:5] pulltype 0=leave at default setting, 1=pullup, 2=pulldown, 3=no pull
|
||||
// [ 7] is_used 1=board uses this pin, 0=not connected and therefore not used
|
||||
|
||||
#define GPIO_MAP_BITS(func,pull,used) ( (uint8_t)( ((func)&0x7) | ((pull)&0x3)<<5 | ((used)&0x1)<<7 ) )
|
||||
|
||||
struct dt_data_t
|
||||
{
|
||||
char* dt_data;
|
||||
uint32_t dt_data_size;
|
||||
};
|
||||
|
||||
#define INFO_MAX_LEN 64
|
||||
typedef struct
|
||||
{
|
||||
char category_name[INFO_MAX_LEN];
|
||||
char product_name[INFO_MAX_LEN];
|
||||
char product_id[INFO_MAX_LEN];
|
||||
char product_version[INFO_MAX_LEN];
|
||||
char product_uuid[INFO_MAX_LEN];
|
||||
char product_vendor[INFO_MAX_LEN];
|
||||
|
||||
uint32_t numeric_serial_number;
|
||||
uint32_t numeric_version;
|
||||
uint32_t numeric_product_id;
|
||||
} hat_board_info_st;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char vendor_name[MAX_STRLEN];
|
||||
char product_name[MAX_STRLEN];
|
||||
int product_id;
|
||||
int product_version;
|
||||
unsigned char* device_tree_buffer;
|
||||
int device_tree_buffer_size;
|
||||
|
||||
// eeprom device
|
||||
eeprom_utils_st dev;
|
||||
|
||||
// buffers (read and write)
|
||||
char* read_buffer;
|
||||
int read_buffer_size;
|
||||
|
||||
char* write_buffer;
|
||||
int write_buffer_size;
|
||||
int write_buffer_used_size;
|
||||
|
||||
// hat initialized
|
||||
bool initialized;
|
||||
|
||||
// eeprom contains valid information (not FFF)
|
||||
bool eeprom_initialized;
|
||||
|
||||
// hat definitions
|
||||
struct header_t header;
|
||||
struct vendor_info_t vinf;
|
||||
struct gpio_map_t gpiomap;
|
||||
struct dt_data_t dt_data;
|
||||
unsigned char* custom_data;
|
||||
|
||||
// temporary date
|
||||
char generated_uuid[128];
|
||||
uint32_t generated_serial;
|
||||
} hat_st;
|
||||
|
||||
int hat_init(hat_st *ee);
|
||||
int hat_close(hat_st *ee);
|
||||
int hat_fill_in(hat_st *ee);
|
||||
int hat_print(hat_st *ee);
|
||||
int hat_generate_write_config(hat_st *ee, bool overwrite);
|
||||
|
||||
// HAT functions after configuration is written and system is
|
||||
// restarted. In this stage the sysfs shall contain the hat definitions
|
||||
int hat_detect_board(hat_board_info_st *info);
|
||||
int hat_detect_from_eeprom(hat_board_info_st *info);
|
||||
void hat_print_board_info(hat_board_info_st *info, bool log);
|
||||
int serial_from_uuid(char* uuid, uint32_t *serial);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // __HAT_H__
|
|
@ -1,38 +0,0 @@
|
|||
#include <stdio.h>
|
||||
#include "../cariboulite_dtbo.h"
|
||||
#include "hat.h"
|
||||
|
||||
|
||||
hat_st hat =
|
||||
{
|
||||
.vendor_name = "CaribouLabs LTD",
|
||||
.product_name = "CaribouLite RPI Hat",
|
||||
.product_id = 0x01,
|
||||
.product_version = 0x01,
|
||||
.device_tree_buffer = cariboulite_dtbo,
|
||||
.device_tree_buffer_size = sizeof(cariboulite_dtbo),
|
||||
|
||||
.dev = {
|
||||
.i2c_address = 0x50, // the i2c address of the eeprom chip
|
||||
.eeprom_type = eeprom_type_24c32,
|
||||
},
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
if (hat_init(&hat) != 0)
|
||||
{
|
||||
printf("error\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
hat_print(&hat);
|
||||
|
||||
hat_board_info_st info = {0};
|
||||
hat_detect_board(&info);
|
||||
hat_print_board_info(&info);
|
||||
|
||||
hat_close(&hat);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
cmake_minimum_required(VERSION 3.1.0)
|
||||
|
||||
project(iir VERSION 1.9.1 LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
set(CMAKE_BUILD_TYPE "RelWithDebInfo")
|
||||
endif()
|
||||
|
||||
cmake_policy(SET CMP0048 NEW) # set VERSION in project()
|
||||
cmake_policy(SET CMP0042 NEW) # enable MACOSX_RPATH by default
|
||||
|
||||
include(GNUInstallDirs)
|
||||
add_subdirectory(test)
|
||||
add_subdirectory(demo)
|
||||
enable_testing ()
|
||||
|
||||
if (MSVC)
|
||||
add_compile_options(/W4)
|
||||
else()
|
||||
add_compile_options(-Wall -Wconversion -Wextra -pedantic)
|
||||
endif()
|
||||
|
||||
set(LIBSRC
|
||||
iir/Biquad.cpp
|
||||
iir/Butterworth.cpp
|
||||
iir/Cascade.cpp
|
||||
iir/ChebyshevI.cpp
|
||||
iir/ChebyshevII.cpp
|
||||
iir/Custom.cpp
|
||||
iir/PoleFilter.cpp
|
||||
iir/RBJ.cpp)
|
||||
|
||||
set(LIBINCLUDE
|
||||
iir/Biquad.h
|
||||
iir/Butterworth.h
|
||||
iir/Cascade.h
|
||||
iir/ChebyshevI.h
|
||||
iir/ChebyshevII.h
|
||||
iir/Common.h
|
||||
iir/Custom.h
|
||||
iir/Layout.h
|
||||
iir/MathSupplement.h
|
||||
iir/PoleFilter.h
|
||||
iir/RBJ.h
|
||||
iir/State.h
|
||||
iir/Types.h)
|
||||
|
||||
add_library(iir SHARED ${LIBSRC})
|
||||
add_library(iir::iir ALIAS iir)
|
||||
|
||||
target_include_directories(iir
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}> # for Iir.h
|
||||
PRIVATE
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/iir> # everything else
|
||||
)
|
||||
|
||||
set_target_properties(iir PROPERTIES
|
||||
SOVERSION 1
|
||||
VERSION ${PROJECT_VERSION}
|
||||
PUBLIC_HEADER Iir.h
|
||||
PRIVATE_HEADER "${LIBINCLUDE}")
|
||||
|
||||
install(TARGETS iir EXPORT iir-targets
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
PRIVATE_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/iir)
|
||||
|
||||
configure_file(iir.pc.in iir.pc @ONLY)
|
||||
|
||||
add_library(iir_static STATIC ${LIBSRC})
|
||||
add_library(iir::iir_static ALIAS iir_static)
|
||||
|
||||
target_include_directories(iir_static
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}> # for Iir.h
|
||||
PRIVATE
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/iir> # everything else
|
||||
)
|
||||
|
||||
set_target_properties(iir_static PROPERTIES
|
||||
POSITION_INDEPENDENT_CODE TRUE
|
||||
VERSION ${PROJECT_VERSION}
|
||||
PUBLIC_HEADER Iir.h
|
||||
PRIVATE_HEADER "${LIBINCLUDE}")
|
||||
|
||||
install(TARGETS iir_static EXPORT iir-targets
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
PRIVATE_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/iir)
|
||||
|
||||
install(EXPORT iir-targets
|
||||
DESTINATION lib/cmake/iir
|
||||
NAMESPACE iir::
|
||||
FILE iir-config.cmake
|
||||
)
|
|
@ -1,22 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2009 Vinnie Falco
|
||||
Copyright (c) 2012 Bernd Porr
|
||||
|
||||
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.
|
|
@ -1,55 +0,0 @@
|
|||
/**
|
||||
*
|
||||
* "A Collection of Useful C++ Classes for Digital Signal Processing"
|
||||
* By Vinnie Falco and Bernd Porr
|
||||
*
|
||||
* Official project location:
|
||||
* https://github.com/berndporr/iir1
|
||||
*
|
||||
* --------------------------------------------------------------------------
|
||||
*
|
||||
* License: MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
* Copyright (c) 2009 by Vinnie Falco
|
||||
* Copyright (c) 2011 by Bernd Porr
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
**/
|
||||
|
||||
#ifndef IIR_H
|
||||
#define IIR_H
|
||||
|
||||
//
|
||||
// Include this file in your application to get everything
|
||||
//
|
||||
|
||||
#include "iir/Common.h"
|
||||
|
||||
#include "iir/Biquad.h"
|
||||
#include "iir/Cascade.h"
|
||||
#include "iir/PoleFilter.h"
|
||||
#include "iir/State.h"
|
||||
|
||||
#include "iir/Butterworth.h"
|
||||
#include "iir/ChebyshevI.h"
|
||||
#include "iir/ChebyshevII.h"
|
||||
#include "iir/Custom.h"
|
||||
#include "iir/RBJ.h"
|
||||
|
||||
#endif
|
|
@ -1,326 +0,0 @@
|
|||
# DSP IIR Realtime C++ filter library
|
||||
|
||||
![alt tag](title.png)
|
||||
|
||||
- High performance
|
||||
- Realtime sample in - sample out processing
|
||||
- Butterworth, RBJ, Chebychev filters
|
||||
- Lowpass, highpass, bandpass and bandstop filters
|
||||
- Template based header-only filter functions
|
||||
- Cross platform: Linux, Windows and Mac
|
||||
|
||||
An infinite impulse response (IIR) filter library for
|
||||
Linux, Mac OSX and Windows
|
||||
which implements Butterworth, RBJ, Chebychev filters
|
||||
and can easily import coefficients generated by Python (scipy).
|
||||
|
||||
The filter processes the data sample by sample for realtime
|
||||
processing.
|
||||
|
||||
It uses templates to allocate the required memory so that
|
||||
it can run without any malloc / new commands.
|
||||
Memory is allocated at compile time
|
||||
so that there is never the risk of memory leaks.
|
||||
|
||||
All realtime filter code is in the header files which guarantees
|
||||
efficient integration into the main program and the compiler
|
||||
can optimise both filter code and main program at the same time.
|
||||
|
||||
## C++ code
|
||||
Add the following include statement to your code:
|
||||
```
|
||||
#include "Iir.h"
|
||||
```
|
||||
|
||||
The general coding approach is that first the filter is
|
||||
instantiated specifying its order, then the
|
||||
parameters are set with the function `setup` and
|
||||
then it's ready to be used for sample by sample realtime filtering.
|
||||
|
||||
### Instantiating the filter
|
||||
The idea is to allocate the memory of the
|
||||
filter at compile time with a template argument to avoid any new
|
||||
commands. This prevents memory leaks and can be optimised at compile
|
||||
time. The `order` provided to the template (for example here for a
|
||||
lowpass filter):
|
||||
```
|
||||
Iir::Butterworth::LowPass<order> f;
|
||||
```
|
||||
is used as the default order by the `setup` command below
|
||||
but can be overridden by a lower order if required.
|
||||
|
||||
### Setting the filter parameters: `setup`
|
||||
All filters are available as lowpass, highpass, bandpass and bandstop/notch
|
||||
filters. Butterworth / Chebyshev offer also low/high/band-shelves with
|
||||
specified passband gain and 0dB gain in the stopband.
|
||||
|
||||
The frequencies can either be analogue ones against the sampling rate
|
||||
or normalised ones between 0..1/2 where 1/2 is the Nyquist frequency. Note
|
||||
that normalised frequencies are simply f = F/Fs and are in units of 1/samples.
|
||||
Internally the library uses normalised frequencies and the setup commands
|
||||
simply divide by the sampling rate if given. Choose between:
|
||||
1. `setup`: sampling rate and the analogue cutoff frequencies
|
||||
2. `setupN`: normalised frequencies in 1/samples between f = 0..1/2 where 1/2 = Nyquist.
|
||||
|
||||
By default `setup` uses the order supplied by the template argument but
|
||||
can be overridden by lower filter orders.
|
||||
|
||||
See the header files in `\iir` or the documentation for the arguments
|
||||
of the `setup` commands.
|
||||
|
||||
The examples below are for lowpass filters:
|
||||
|
||||
1. Butterworth -- `Butterworth.h`
|
||||
Standard filter suitable for most applications. Monotonic response.
|
||||
```
|
||||
const int order = 4; // 4th order (=2 biquads)
|
||||
Iir::Butterworth::LowPass<order> f;
|
||||
const float samplingrate = 1000; // Hz
|
||||
const float cutoff_frequency = 5; // Hz
|
||||
f.setup (samplingrate, cutoff_frequency);
|
||||
```
|
||||
or specify a normalised frequency between 0..1/2:
|
||||
```
|
||||
f.setupN(norm_cutoff_frequency);
|
||||
```
|
||||
|
||||
2. Chebyshev Type I -- `ChebyshevI.h`
|
||||
With permissible passband ripple in dB.
|
||||
```
|
||||
Iir::ChebyshevI::LowPass<order> f;
|
||||
const float passband_ripple_in_db = 5;
|
||||
f.setup (samplingrate,
|
||||
cutoff_frequency,
|
||||
passband_ripple_in_dB);
|
||||
```
|
||||
or specify a normalised frequency between 0..1/2:
|
||||
```
|
||||
f.setupN(norm_cutoff_frequency,passband_ripple_in_dB);
|
||||
```
|
||||
|
||||
|
||||
3. Chebyshev Type II -- `ChebyshevII.h`
|
||||
With worst permissible stopband rejection in dB.
|
||||
```
|
||||
Iir::ChebyshevII::LowPass<order> f;
|
||||
double stopband_ripple_in_dB = 20;
|
||||
f.setup (samplingrate,
|
||||
cutoff_frequency,
|
||||
stopband_ripple_in_dB);
|
||||
```
|
||||
or specify a normalised frequency between 0..1/2:
|
||||
```
|
||||
f.setupN(norm_cutoff_frequency,stopband_ripple_in_dB);
|
||||
```
|
||||
|
||||
4. RBJ -- `RBJ.h`
|
||||
2nd order filters with cutoff and Q factor.
|
||||
```
|
||||
Iir::RBJ::LowPass f;
|
||||
const float cutoff_frequency = 100;
|
||||
const float Q_factor = 5;
|
||||
f.setup (samplingrate, cutoff_frequency, Q_factor);
|
||||
```
|
||||
or specify a normalised frequency between 0..1/2:
|
||||
```
|
||||
f.setupN(norm_cutoff_frequency, Q_factor);
|
||||
```
|
||||
|
||||
5. Designing filters with Python's scipy.signal -- `Custom.h`
|
||||
```
|
||||
########
|
||||
# Python
|
||||
# See "elliptic_design.py" for the complete code.
|
||||
from scipy import signal
|
||||
order = 4
|
||||
sos = signal.ellip(order, 5, 40, 0.2, 'low', output='sos')
|
||||
print(sos) # copy/paste the coefficients over & replace [] with {}
|
||||
|
||||
///////
|
||||
// C++
|
||||
// part of "iirdemo.cpp"
|
||||
const double coeff[][6] = {
|
||||
{1.665623674062209972e-02,
|
||||
-3.924801366970616552e-03,
|
||||
1.665623674062210319e-02,
|
||||
1.000000000000000000e+00,
|
||||
-1.715403014004022175e+00,
|
||||
8.100474793174089472e-01},
|
||||
{1.000000000000000000e+00,
|
||||
-1.369778997100624895e+00,
|
||||
1.000000000000000222e+00,
|
||||
1.000000000000000000e+00,
|
||||
-1.605878925999785656e+00,
|
||||
9.538657786383895054e-01}
|
||||
};
|
||||
const int nSOS = sizeof(coeff) / sizeof(coeff[0]); // here: nSOS = 2 = order / 2
|
||||
Iir::Custom::SOSCascade<nSOS> cust(coeff);
|
||||
```
|
||||
|
||||
### Realtime filtering sample by sample
|
||||
Samples are processed one by one. In the example below
|
||||
a sample `x` is processed with the `filter`
|
||||
command and then saved in `y`. The types of `x` and `y` can either be
|
||||
float or double
|
||||
(integer is also allowed but is still processed internally as floating point):
|
||||
```
|
||||
y = f.filter(x);
|
||||
```
|
||||
This is then repeated for every incoming sample in a
|
||||
loop or event handler.
|
||||
|
||||
|
||||
### Error handling
|
||||
Invalid values provided to `setup()` will throw
|
||||
an exception. Parameters provided to `setup()` which
|
||||
result in coefficients being NAN will also
|
||||
throw an exception.
|
||||
|
||||
|
||||
## Linking
|
||||
|
||||
### CMake setup
|
||||
If you use cmake as your build system then just add
|
||||
to your `CMakeLists.txt` the following lines for the dynamic library:
|
||||
```
|
||||
find_package(iir)
|
||||
target_link_libraries(... iir::iir)
|
||||
```
|
||||
or for the static one:
|
||||
```
|
||||
find_package(iir)
|
||||
target_link_libraries(... iir::iir_static)
|
||||
```
|
||||
|
||||
### Generic linker setup
|
||||
Link it against the dynamic library
|
||||
(Unix/Mac: `-liir`, Windows: `iir.lib`)
|
||||
or the static library (Unix/Mac: `libiir_static.a`,
|
||||
Windows: `libiir_static.lib`).
|
||||
|
||||
|
||||
|
||||
## Packages for Ubuntu (xenial / bionic / focal):
|
||||
|
||||
If you have Ubuntu's LTS distros xenial, bionic or focal then
|
||||
install it as a pre-compiled package:
|
||||
|
||||
```
|
||||
sudo add-apt-repository ppa:berndporr/dsp
|
||||
```
|
||||
|
||||
It's available for 32,64 bit PC and 32,64 bit ARM (Raspberry PI etc).
|
||||
The documentation and the example programs are in:
|
||||
```
|
||||
/usr/share/doc/iir1-dev/
|
||||
```
|
||||
|
||||
## Package for MacOS
|
||||
|
||||
Make sure you have the homebrew package manager installed: https://brew.sh/
|
||||
|
||||
Add the homebrew tap:
|
||||
|
||||
```
|
||||
brew tap berndporr/dsp
|
||||
```
|
||||
|
||||
and then install the iir filter package with:
|
||||
|
||||
```
|
||||
brew install iir
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Compilation from source
|
||||
|
||||
The build tool is `cmake` which generates the make- or project
|
||||
files for the different platforms. `cmake` is available for
|
||||
Linux, Windows and Mac. It also compiles directly on a
|
||||
Raspberry PI.
|
||||
|
||||
### Linux / Mac
|
||||
|
||||
Run
|
||||
```
|
||||
cmake .
|
||||
```
|
||||
which generates the Makefile. Then run:
|
||||
```
|
||||
make
|
||||
sudo make install
|
||||
```
|
||||
which installs it under `/usr/local/lib` and `/usr/local/include`.
|
||||
|
||||
Both gcc and clang have been tested.
|
||||
|
||||
### Windows
|
||||
|
||||
```
|
||||
cmake -G "Visual Studio 16 2019" -A x64 .
|
||||
```
|
||||
|
||||
See `cmake` for the different build-options. Above is for a 64 bit build.
|
||||
Then start Visual C++ and open the solution. This will create
|
||||
the DLL and the LIB files. Under Windows it's highly recommended
|
||||
to use the static library and link it into the application program.
|
||||
|
||||
### Unit tests
|
||||
|
||||
Run unit tests by typing `make test` or just `ctest`.
|
||||
These test if after a delta pulse all filters relax to zero,
|
||||
that their outputs never become NaN and if the Direct Form I&II filters calculate
|
||||
expected sequences by comparing them from results created
|
||||
by the output of scipy's `sosfilt`.
|
||||
|
||||
## Documentation
|
||||
|
||||
### Learn from the demos
|
||||
The easiest way to learn is from the examples which are in the `demo`
|
||||
directory. A delta pulse as a test signal is sent into the different
|
||||
filters and saved in a file. With the Python script
|
||||
`plot_impulse_fresponse.py` you can then plot the frequency responses.
|
||||
|
||||
Also the directory containing the unit tests provides examples for
|
||||
every filter type.
|
||||
|
||||
### Detailed documentation
|
||||
A PDF of all classes, methods and in particular `setup` functions
|
||||
is in the `docs/pdf` directory.
|
||||
|
||||
The online documentation is here: http://berndporr.github.io/iir1
|
||||
|
||||
## Example filter responses
|
||||
|
||||
These responses have been generated by `iirdemo.cpp`
|
||||
in the `/demo/` directory and then plotted with `plot_impulse_fresponse.py`.
|
||||
|
||||
![alt tag](demo/1.png)
|
||||
![alt tag](demo/2.png)
|
||||
![alt tag](demo/3.png)
|
||||
![alt tag](demo/4.png)
|
||||
![alt tag](demo/5.png)
|
||||
![alt tag](demo/7.png)
|
||||
![alt tag](demo/8.png)
|
||||
|
||||
## Credits
|
||||
|
||||
This library has been further developed from Vinnie Falco's
|
||||
great original work which can be found here:
|
||||
|
||||
https://github.com/vinniefalco/DSPFilters
|
||||
|
||||
While the original library processes audio arrays this
|
||||
library has been adapted to do fast realtime processing sample
|
||||
by sample. The `setup`
|
||||
command won't require the filter order and instead remembers
|
||||
it from the template argument. The class structure has
|
||||
been simplified and all functions documented for doxygen.
|
||||
Instead of having assert() statements this libary throws
|
||||
exceptions in case a parameter is wrong. Any filter design
|
||||
requiring optimisation (for example Ellipic filters) has
|
||||
been removed and instead a function has been added which can import easily
|
||||
coefficients from scipy.
|
||||
|
||||
Bernd Porr -- http://www.berndporr.me.uk
|
Przed Szerokość: | Wysokość: | Rozmiar: 57 KiB |
Przed Szerokość: | Wysokość: | Rozmiar: 50 KiB |
Przed Szerokość: | Wysokość: | Rozmiar: 52 KiB |
Przed Szerokość: | Wysokość: | Rozmiar: 68 KiB |
Przed Szerokość: | Wysokość: | Rozmiar: 68 KiB |
Przed Szerokość: | Wysokość: | Rozmiar: 60 KiB |
Przed Szerokość: | Wysokość: | Rozmiar: 49 KiB |
|
@ -1,19 +0,0 @@
|
|||
project(IIRDemo)
|
||||
|
||||
cmake_minimum_required(VERSION 3.1.0)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
if (MSVC)
|
||||
add_compile_options(/W4 /WX)
|
||||
else()
|
||||
add_compile_options(-Wall -Wextra -pedantic -Werror)
|
||||
endif()
|
||||
|
||||
add_executable (iirdemo iirdemo.cpp)
|
||||
target_link_libraries(iirdemo iir_static)
|
||||
target_include_directories(iirdemo PRIVATE ..)
|
||||
|
||||
add_executable (ecg50hzfilt ecg50hzfilt.cpp)
|
||||
target_link_libraries(ecg50hzfilt iir_static)
|
||||
target_include_directories(ecg50hzfilt PRIVATE ..)
|
|
@ -1,27 +0,0 @@
|
|||
# Coding examples
|
||||
|
||||
This demo directory shows how filters are set up, how data is
|
||||
processed sample by sample and how the frequency responses
|
||||
look like with the help of python scripts.
|
||||
|
||||
## Impulse responses and spectra of different filters:
|
||||
```
|
||||
./iirdemo
|
||||
python3 plot_impulse_fresponse.py
|
||||
```
|
||||
|
||||
## 50Hz removal and lowpass filtering:
|
||||
```
|
||||
./ecg50hzfilt
|
||||
python3 plot_ecg_filt.py
|
||||
```
|
||||
|
||||
## Optimisation tips
|
||||
|
||||
The actual filter operations are header-only which means that
|
||||
they will never be pre-compiled in the library but the compiler
|
||||
can optimise the filter routine for your local processor. Consider
|
||||
using:
|
||||
```
|
||||
add_compile_options(-O3 -march=native)
|
||||
```
|
|
@ -1,21 +0,0 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
# Calculates the coefficients for a Bessel filter
|
||||
|
||||
from scipy import signal
|
||||
# sampling rate
|
||||
fs = 1000
|
||||
# cutoff
|
||||
f0 = 100
|
||||
# order
|
||||
order = 8
|
||||
sos = signal.bessel(order, f0/fs*2, 'low', output='sos')
|
||||
for s in sos:
|
||||
print("{",end="")
|
||||
n = 0
|
||||
for c in s:
|
||||
print("%.18e" % c,end="")
|
||||
n=n+1
|
||||
if n<6:
|
||||
print(",",end="")
|
||||
print("},")
|
Przed Szerokość: | Wysokość: | Rozmiar: 91 KiB |
Przed Szerokość: | Wysokość: | Rozmiar: 80 KiB |