From 4dbdb5ddbbd2abed2805cbbde5a45b1f88447a2c Mon Sep 17 00:00:00 2001 From: Silvano Seva Date: Wed, 25 Nov 2020 16:40:17 +0100 Subject: [PATCH] Implemented reading of channel data from external flash memory for MDx targets --- meson.build | 2 +- openrtx/include/cps.h | 114 +++++++++++++++++++ openrtx/include/interfaces/nvmem.h | 9 ++ platform/drivers/NVM/calibInfo_MDx.h | 141 +++++++++++++++++++++++ platform/drivers/NVM/extFlash_MDx.c | 1 - platform/drivers/NVM/nvmem_MD3x0.c | 145 ++++++++++++++++++++++++ platform/drivers/NVM/nvmem_MDUV3x0.c | 161 +++++++++++++++++++++++++++ tests/platform/printContacts_MDx.c | 52 +++++++++ 8 files changed, 623 insertions(+), 2 deletions(-) create mode 100644 openrtx/include/cps.h create mode 100644 platform/drivers/NVM/calibInfo_MDx.h create mode 100644 tests/platform/printContacts_MDx.c diff --git a/meson.build b/meson.build index 9f89f5ff..9cd4dd34 100644 --- a/meson.build +++ b/meson.build @@ -11,7 +11,7 @@ project('OpenRTX', 'c', ## OpenRTX -openrtx_src = ['tests/platform/printCalib_MDx.c', +openrtx_src = ['tests/platform/printContacts_MDx.c', 'openrtx/src/bootstrap.c', 'openrtx/src/state.c', 'openrtx/src/ui.c', diff --git a/openrtx/include/cps.h b/openrtx/include/cps.h new file mode 100644 index 00000000..2859a988 --- /dev/null +++ b/openrtx/include/cps.h @@ -0,0 +1,114 @@ +/*************************************************************************** + * Copyright (C) 2020 by Federico Amedeo Izzo IU2NUO, * + * Niccolò Izzo IU2KIN, * + * Frederik Saraci IU2NRO, * + * Silvano Seva IU2KWO * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, see * + ***************************************************************************/ + +#ifndef CPS_H +#define CPS_H + +#include +#include + +/** + * \enum mode_t Enumeration type defining the operating mode associated to each + * channel. + */ +enum mode_t +{ + FM = 0, /**< Analog FM mode */ + DMR /**< DMR mode */ +}; + +/** + * \enum admit_t Enumeration type defining the admission criteria to a the + * channel. + */ +enum admit_t +{ + ALWAYS = 0, /**< Always transmit when PTT is pressed */ + FREE, /**< Transmit only if channel si free */ + TONE, /**< Transmit on matching tone */ + COLOR /**< Transmit only if color code is not used yet */ +}; + +/** + * \enum bw_t Enumeration type defining the bandwidth of a the channel. + */ +enum bw_t +{ + BW_12_5 = 0, /**< Bandwidth is 12.5kHz */ + BW_20, /**< Bandwidth is 20kHz */ + BW_25 /**< Bandwidth is 25kHz */ +}; + +/** + * Data structure containing all and only the information for analog FM channels, + * like CTC/DCS tones. + */ +typedef struct +{ + uint16_t ctcDcs_rx; /**< RX CTCSS or DCS code, squelch opens on match */ + uint16_t ctcDcs_tx; /**< TX CTCSS or DCS code, sent alongside voice */ +} fmInfo_t; + +/** + * Data structure containing all and only the information for DMR channels. + */ +typedef struct +{ + uint8_t rxColorCode : 4, /**< Color code for RX squelch opening */ + txColorCode : 4; /**< Color code sent during transmission */ + + uint8_t dmr_timeslot; /**< DMR timeslot, either 1 or 2 */ + uint16_t contactName_index; /**< Index to retrieve data from contact list */ +} dmrInfo_t; + +/** + * Data structure containing all the information of a channel, either FM or DMR. + */ +typedef struct +{ + uint8_t mode : 1, /**< Operating mode */ + bandwidth : 2, /**< Bandwidth */ + admit_criteria : 2, /**< Admit criterion */ + squelch : 1, /**< Squelch type: 0 = tight, 1 = normal */ + rx_only : 1, /**< 1 means RX-only channel */ + vox : 1; /**< VOX enable */ + + float power; /**< Transmission power, in watt */ + + freq_t rx_frequency; /**< RX Frequency, in Hz */ + freq_t tx_frequency; /**< TX Frequency, in Hz */ + + uint8_t tot; /**< TOT x 15sec: 0-Infinite, 1=15s...33=495s */ + uint8_t tot_rekey_delay; /**< TOT Rekey Delay: 0...255s */ + + uint8_t emSys_index; /**< Emergency System: None, System1...32 */ + uint8_t scanList_index; /**< Scan List: None, ScanList1...250 */ + uint8_t groupList_index; /**< Group List: None, GroupList1...128 */ + + char name[16]; /**< Channel name */ + + union + { + fmInfo_t fm; /**< Information block for FM channels */ + dmrInfo_t dmr; /**< Information block for DMR channels */ + }; +} channel_t; + +#endif diff --git a/openrtx/include/interfaces/nvmem.h b/openrtx/include/interfaces/nvmem.h index 65692b86..67a81996 100644 --- a/openrtx/include/interfaces/nvmem.h +++ b/openrtx/include/interfaces/nvmem.h @@ -22,6 +22,7 @@ #define NVMEM_H #include +#include /** * Interface for nonvolatile memory management, usually an external SPI flash @@ -45,5 +46,13 @@ void nvm_terminate(); */ void nvm_readCalibData(void *buf); +/** + * Read one channel entry from table stored in nonvolatile memory. + * + * @param channel: pointer to the channel_t data structure to be populated. + * @param pos: position, inside the channel table, from which read data. + * @return 0 on success, -1 on failure + */ +int nvm_readChannelData(channel_t *channel, uint16_t pos); #endif /* NVMEM_H */ diff --git a/platform/drivers/NVM/calibInfo_MDx.h b/platform/drivers/NVM/calibInfo_MDx.h new file mode 100644 index 00000000..ac46b662 --- /dev/null +++ b/platform/drivers/NVM/calibInfo_MDx.h @@ -0,0 +1,141 @@ +/*************************************************************************** + * Copyright (C) 2020 by Federico Amedeo Izzo IU2NUO, * + * Niccolò Izzo IU2KIN * + * Frederik Saraci IU2NRO * + * Silvano Seva IU2KWO * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, see * + ***************************************************************************/ + +#ifndef CALIBINFO_MDX_H +#define CALIBINFO_MDX_H + +#include +#include + +/** + * Data types defining the structure of calibration data stored in external + * flash memory of MDx devices. + */ + +/** + * \brief Calibration data for MD-3x0. + */ +typedef struct +{ + uint8_t vox1; + uint8_t vox10; + uint8_t rxLowVoltage; + uint8_t rxFullVoltage; + uint8_t rssi1; + uint8_t rssi4; + uint8_t analogMic; + uint8_t digitalMic; + uint8_t freqAdjustHigh; + uint8_t freqAdjustMid; + uint8_t freqAdjustLow; + freq_t rxFreq[9]; + freq_t txFreq[9]; + uint8_t txHighPower[9]; + uint8_t txLowPower[9]; + uint8_t rxSensitivity[9]; + uint8_t openSql9[9]; + uint8_t closeSql9[9]; + uint8_t openSql1[9]; + uint8_t closeSql1[9]; + uint8_t maxVolume[9]; + uint8_t ctcss67Hz[9]; + uint8_t ctcss151Hz[9]; + uint8_t ctcss254Hz[9]; + uint8_t dcsMod2[9]; + uint8_t dcsMod1[9]; + uint8_t mod1Partial[9]; + uint8_t analogVoiceAdjust[9]; + uint8_t lockVoltagePartial[9]; + uint8_t sendIpartial[9]; + uint8_t sendQpartial[9]; + uint8_t sendIrange[9]; + uint8_t sendQrange[9]; + uint8_t rxIpartial[9]; + uint8_t rxQpartial[9]; + uint8_t analogSendIrange[9]; + uint8_t analogSendQrange[9]; +} +md3x0Calib_t; + +typedef struct +{ + uint8_t freqAdjustMid; + freq_t rxFreq[5]; + freq_t txFreq[5]; + uint8_t txHighPower[5]; + uint8_t txMidPower[5]; + uint8_t txLowPower[5]; + uint8_t rxSensitivity[5]; + uint8_t openSql9[5]; + uint8_t closeSql9[5]; + uint8_t openSql1[5]; + uint8_t closeSql1[5]; + uint8_t ctcss67Hz[5]; + uint8_t ctcss151Hz[5]; + uint8_t ctcss254Hz[5]; + uint8_t dcsMod1[5]; + uint8_t sendIrange[5]; + uint8_t sendQrange[5]; + uint8_t analogSendIrange[5]; + uint8_t analogSendQrange[5]; +} +VhfCalib_t; + +typedef struct +{ + uint8_t freqAdjustMid; + freq_t rxFreq[9]; + freq_t txFreq[9]; + uint8_t txHighPower[9]; + uint8_t txMidPower[9]; + uint8_t txLowPower[9]; + uint8_t rxSensitivity[9]; + uint8_t openSql9[9]; + uint8_t closeSql9[9]; + uint8_t openSql1[9]; + uint8_t closeSql1[9]; + uint8_t ctcss67Hz[9]; + uint8_t ctcss151Hz[9]; + uint8_t ctcss254Hz[9]; + uint8_t dcsMod1[9]; + uint8_t sendIrange[9]; + uint8_t sendQrange[9]; + uint8_t analogSendIrange[9]; + uint8_t analogSendQrange[9]; +} +UhfCalib_t; + +/** + * \brief Calibration data for MD-UV3x0. + */ +typedef struct +{ + uint8_t vox1; + uint8_t vox10; + uint8_t rxLowVoltage; + uint8_t rxFullVoltage; + uint8_t rssi1; + uint8_t rssi4; + VhfCalib_t vhfCal; + UhfCalib_t uhfCal; +} +mduv3x0Calib_t; + +#endif /* CALIBINFO_MDX_H */ diff --git a/platform/drivers/NVM/extFlash_MDx.c b/platform/drivers/NVM/extFlash_MDx.c index 72998e02..170245a1 100644 --- a/platform/drivers/NVM/extFlash_MDx.c +++ b/platform/drivers/NVM/extFlash_MDx.c @@ -125,7 +125,6 @@ void extFlash_readData(uint32_t addr, uint8_t* buf, size_t len) (void) _spi1SendRecv((addr >> 16) & 0xFF); /* Address high */ (void) _spi1SendRecv((addr >> 8) & 0xFF); /* Address middle */ (void) _spi1SendRecv(addr & 0xFF); /* Address low */ - (void) _spi1SendRecv(0x00); /* Dummy byte */ for(size_t i = 0; i < len; i++) { diff --git a/platform/drivers/NVM/nvmem_MD3x0.c b/platform/drivers/NVM/nvmem_MD3x0.c index 01088b81..308f5f7f 100644 --- a/platform/drivers/NVM/nvmem_MD3x0.c +++ b/platform/drivers/NVM/nvmem_MD3x0.c @@ -23,6 +23,98 @@ #include "extFlash_MDx.h" #include "calibInfo_MDx.h" +/** + * \internal Data structure matching the one used by original MD3x0 firmware to + * manage channel data inside nonvolatile flash memory. + * + * Taken by dmrconfig repository: https://github.com/sergev/dmrconfig/blob/master/md380.c + */ +typedef struct +{ + // Byte 0 + uint8_t channel_mode : 2, + bandwidth : 2, + autoscan : 1, + squelch : 1, + _unused1 : 1, + lone_worker : 1; + + // Byte 1 + uint8_t talkaround : 1, + rx_only : 1, + repeater_slot : 2, + colorcode : 4; + + // Byte 2 + uint8_t privacy_no : 4, + privacy : 2, + private_call_conf : 1, + data_call_conf : 1; + + // Byte 3 + uint8_t rx_ref_frequency : 2, + _unused2 : 1, + emergency_alarm_ack : 1, + _unused3 : 2, + uncompressed_udp : 1, + display_pttid_dis : 1; + + // Byte 4 + uint8_t tx_ref_frequency : 2, + _unused4 : 2, + vox : 1, + power : 1, + admit_criteria : 2; + + // Byte 5 + uint8_t _unused5 : 4, + in_call_criteria : 2, + _unused6 : 2; + + // Bytes 6-7 + uint16_t contact_name_index; + + // Bytes 8-9 + uint8_t tot : 6, + _unused13 : 2; + uint8_t tot_rekey_delay; + + // Bytes 10-11 + uint8_t emergency_system_index; + uint8_t scan_list_index; + + // Bytes 12-13 + uint8_t group_list_index; + uint8_t _unused7; + + // Bytes 14-15 + uint8_t _unused8; + uint8_t _unused9; + + // Bytes 16-23 + uint32_t rx_frequency; + uint32_t tx_frequency; + + // Bytes 24-27 + uint16_t ctcss_dcs_receive; + uint16_t ctcss_dcs_transmit; + + // Bytes 28-29 + uint8_t rx_signaling_syst; + uint8_t tx_signaling_syst; + + // Bytes 30-31 + uint8_t _unused10; + uint8_t _unused11; + + // Bytes 32-63 + uint16_t name[16]; +} +md3x0Channel_t; + +const uint32_t chDataBaseAddr = 0x1ee00; /**< Base address of channel data */ +const uint32_t maxNumChannels = 1000; /**< Maximum number of channels in memory */ + /** * \internal Utility function to convert 4 byte BCD values into a 32-bit * unsigned integer ones. @@ -93,3 +185,56 @@ void nvm_readCalibData(void *buf) calib->txFreq[i] = ((freq_t) _bcd2bin(freqs[2*i+1])); } } + +int nvm_readChannelData(channel_t *channel, uint16_t pos) +{ + if(pos > maxNumChannels) return -1; + + extFlash_wakeup(); + delayUs(5); + + md3x0Channel_t chData; + uint32_t readAddr = chDataBaseAddr + pos * sizeof(md3x0Channel_t); + extFlash_readData(readAddr, ((uint8_t *) &chData), sizeof(md3x0Channel_t)); + extFlash_sleep(); + + channel->mode = chData.channel_mode - 1; + channel->bandwidth = chData.bandwidth; + channel->admit_criteria = chData.admit_criteria; + channel->squelch = chData.squelch; + channel->rx_only = chData.rx_only; + channel->vox = chData.vox; + channel->power = ((chData.power == 1) ? 5.0f : 1.0f); + channel->rx_frequency = _bcd2bin(chData.rx_frequency); + channel->tx_frequency = _bcd2bin(chData.tx_frequency); + channel->tot = chData.tot; + channel->tot_rekey_delay = chData.tot_rekey_delay; + channel->emSys_index = chData.emergency_system_index; + channel->scanList_index = chData.scan_list_index; + channel->groupList_index = chData.group_list_index; + + /* + * Brutally convert channel name from unicode to char by truncating the most + * significant byte + */ + for(uint16_t i = 0; i < 16; i++) + { + channel->name[i] = ((char) (chData.name[i] & 0x00FF)); + } + + /* Load mode-specific parameters */ + if(channel->mode == FM) + { + channel->fm.ctcDcs_rx = chData.ctcss_dcs_receive; + channel->fm.ctcDcs_tx = chData.ctcss_dcs_transmit; + } + else if(channel->mode == DMR) + { + channel->dmr.contactName_index = chData.contact_name_index; + channel->dmr.dmr_timeslot = chData.repeater_slot; + channel->dmr.rxColorCode = chData.colorcode; + channel->dmr.txColorCode = chData.colorcode; + } + + return 0; +} diff --git a/platform/drivers/NVM/nvmem_MDUV3x0.c b/platform/drivers/NVM/nvmem_MDUV3x0.c index 6ce2dd6c..314c51d3 100644 --- a/platform/drivers/NVM/nvmem_MDUV3x0.c +++ b/platform/drivers/NVM/nvmem_MDUV3x0.c @@ -23,6 +23,102 @@ #include "calibInfo_MDx.h" #include "extFlash_MDx.h" +/** + * \internal Data structure matching the one used by original MD3x0 firmware to + * manage channel data inside nonvolatile flash memory. + * + * Taken by dmrconfig repository: https://github.com/sergev/dmrconfig/blob/master/uv380.c + */ +typedef struct +{ + // Byte 0 + uint8_t channel_mode : 2, + bandwidth : 2, + autoscan : 1, + _unused1 : 2, + lone_worker : 1; + + // Byte 1 + uint8_t _unused2 : 1, + rx_only : 1, + repeater_slot : 2, + colorcode : 4; + + // Byte 2 + uint8_t privacy_no : 4, + privacy : 2, + private_call_conf : 1, + data_call_conf : 1; + + // Byte 3 + uint8_t rx_ref_frequency : 2, + _unused3 : 1, + emergency_alarm_ack : 1, + _unused4 : 3, + display_pttid_dis : 1; + + // Byte 4 + uint8_t tx_ref_frequency : 2, + _unused5 : 2, + vox : 1, + _unused6 : 1, + admit_criteria : 2; + + // Byte 5 + uint8_t _unused7 : 4, + in_call_criteria : 2, + turn_off_freq : 2; + + // Bytes 6-7 + uint16_t contact_name_index; + + // Bytes 8-9 + uint8_t tot : 6, + _unused13 : 2; + uint8_t tot_rekey_delay; + + // Bytes 10-11 + uint8_t emergency_system_index; + uint8_t scan_list_index; + + // Bytes 12-13 + uint8_t group_list_index; + uint8_t _unused8; + + // Bytes 14-15 + uint8_t _unused9; + uint8_t squelch; + + // Bytes 16-23 + uint32_t rx_frequency; + uint32_t tx_frequency; + + // Bytes 24-27 + uint16_t ctcss_dcs_receive; + uint16_t ctcss_dcs_transmit; + + // Bytes 28-29 + uint8_t rx_signaling_syst; + uint8_t tx_signaling_syst; + + // Byte 30 + uint8_t power : 2, + _unused10 : 6; + + // Byte 31 + uint8_t _unused11 : 3, + dcdm_switch_dis : 1, + leader_ms : 1, + _unused12 : 3; + + // Bytes 32-63 + uint16_t name[16]; +} +mduv3x0Channel_t; + +const uint32_t chDataBaseAddr = 0x40000; /**< Base address of channel data */ +const uint32_t maxNumChannels = 3000; /**< Maximum number of channels in memory */ + /** * \internal Utility function to convert 4 byte BCD values into a 32-bit * unsigned integer ones. @@ -115,3 +211,68 @@ void nvm_readCalibData(void *buf) calib->vhfCal.txFreq[i] = ((freq_t) _bcd2bin(freqs[2*i+1])); } } + +int nvm_readChannelData(channel_t *channel, uint16_t pos) +{ + if(pos > maxNumChannels) return -1; + + extFlash_wakeup(); + delayUs(5); + + mduv3x0Channel_t chData; + uint32_t readAddr = chDataBaseAddr + pos * sizeof(mduv3x0Channel_t); + extFlash_readData(readAddr, ((uint8_t *) &chData), sizeof(mduv3x0Channel_t)); + extFlash_sleep(); + + channel->mode = chData.channel_mode - 1; + channel->bandwidth = chData.bandwidth; + channel->admit_criteria = chData.admit_criteria; + channel->squelch = chData.squelch; + channel->rx_only = chData.rx_only; + channel->vox = chData.vox; + channel->rx_frequency = _bcd2bin(chData.rx_frequency); + channel->tx_frequency = _bcd2bin(chData.tx_frequency); + channel->tot = chData.tot; + channel->tot_rekey_delay = chData.tot_rekey_delay; + channel->emSys_index = chData.emergency_system_index; + channel->scanList_index = chData.scan_list_index; + channel->groupList_index = chData.group_list_index; + + if(chData.power == 3) + { + channel->power = 5.0f; /* High power -> 5W */ + } + else if(chData.power == 2) + { + channel->power = 2.5f; /* Mid power -> 2.5W */ + } + else + { + channel->power = 1.0f; /* Low power -> 1W */ + } + + /* + * Brutally convert channel name from unicode to char by truncating the most + * significant byte + */ + for(uint16_t i = 0; i < 16; i++) + { + channel->name[i] = ((char) (chData.name[i] & 0x00FF)); + } + + /* Load mode-specific parameters */ + if(channel->mode == FM) + { + channel->fm.ctcDcs_rx = chData.ctcss_dcs_receive; + channel->fm.ctcDcs_tx = chData.ctcss_dcs_transmit; + } + else if(channel->mode == DMR) + { + channel->dmr.contactName_index = chData.contact_name_index; + channel->dmr.dmr_timeslot = chData.repeater_slot; + channel->dmr.rxColorCode = chData.colorcode; + channel->dmr.txColorCode = chData.colorcode; + } + + return 0; +} diff --git a/tests/platform/printContacts_MDx.c b/tests/platform/printContacts_MDx.c new file mode 100644 index 00000000..8bcb77e6 --- /dev/null +++ b/tests/platform/printContacts_MDx.c @@ -0,0 +1,52 @@ +/*************************************************************************** + * Copyright (C) 2020 by Federico Amedeo Izzo IU2NUO, * + * Niccolò Izzo IU2KIN, * + * Frederik Saraci IU2NRO, * + * Silvano Seva IU2KWO * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 3 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, see * + ***************************************************************************/ + +#include +#include +#include +#include +#include + +int main() +{ + nvm_init(); + + uint16_t pos = 0; + + while(1) + { + getchar(); + + channel_t ch; + nvm_readChannelData(&ch, pos); + printf("Contact entry %d:\r\n", pos+1); + printf(" %s\r\n TX: %ld\r\n RX: %ld\r\n Mode: %s\r\n Bandwidth: %s\r\n", + ch.name, + ch.tx_frequency, + ch.rx_frequency, + (ch.mode == 1) ? "DMR" : "FM", + (ch.bandwidth == BW_12_5) ? "12.5kHz" : ((ch.bandwidth == BW_20) + ? "20kHz" : "25kHz")); + puts("\r"); + pos += 1; + } + + return 0; +}