UHSDR/UHSDR-active-devel/mchf-eclipse/hardware/uhsdr_board.c
2022-11-08 16:13:55 +01:00

1118 lines
29 KiB
C

/* -*- mode: c; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; coding: utf-8 -*- */
/************************************************************************************
** **
** mcHF QRP Transceiver **
** K Atanassov - M0NKA 2014 **
** **
**---------------------------------------------------------------------------------**
** **
** File name: **
** Description: **
** Last Modified: **
** Licence: GNU GPLv3 **
************************************************************************************/
#include "uhsdr_board.h"
#include "ui_configuration.h"
#include "ui_lcd_hy28.h"
#include <stdio.h>
#include "uhsdr_hw_i2c.h"
#include "uhsdr_rtc.h"
#include "ui_driver.h"
#include "ui_rotary.h"
#include "codec.h"
#include "soft_tcxo.h"
//
// Eeprom items
#include "uhsdr_flash.h"
#include "adc.h"
#include "dac.h"
#include "uhsdr_keypad.h"
#include "osc_si5351a.h"
// Transceiver state public structure
__IO __MCHF_SPECIALMEM TransceiverState ts;
//TransceiverState ts;
static void Board_Led_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Pull = GPIO_PULLUP;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStructure.Pin = GREEN_LED;
HAL_GPIO_Init(GREEN_LED_PIO, &GPIO_InitStructure);
GPIO_InitStructure.Pin = RED_LED;
HAL_GPIO_Init(RED_LED_PIO, &GPIO_InitStructure);
}
#if 0
// DO NOT ENABLE UNLESS ALL TOUCHSCREEN SETUP CODE IS DISABLED
// TOUCHSCREEN AND USART SHARE PA9 Pin
static void mchf_board_debug_init(void)
{
#ifdef DEBUG_BUILD
// disabled the USART since it is used by the touch screen code
// as well which renders it unusable
#error "Debug Build No Longer Supported, needs alternative way of communication"
USART_InitTypeDef USART_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitStructure.USART_BaudRate = 921600;//230400;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Tx;
// Enable UART clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
// Connect PXx to USARTx_Tx
GPIO_PinAFConfig(DEBUG_PRINT_PIO, DEBUG_PRINT_SOURCE, GPIO_AF_USART1);
// Configure USART Tx as alternate function
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Pin = DEBUG_PRINT;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DEBUG_PRINT_PIO, &GPIO_InitStructure);
// USART configuration
USART_Init(USART1, &USART_InitStructure);
// Enable USART
USART_Cmd(USART1, ENABLE);
// Wait tx ready
while (USART_GetFlagStatus(DEBUG_COM, USART_FLAG_TC) == RESET);
#endif
}
#endif
static void Board_TxRxCntrPin_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Pull = GPIO_PULLDOWN;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
// RX/TX control pin init
GPIO_InitStructure.Pin = TXRX_CNTR;
HAL_GPIO_Init(TXRX_CNTR_PIO, &GPIO_InitStructure);
}
static void Board_Dac_Init(void)
{
#ifdef UI_BRD_OVI40
HAL_DAC_Start(&hdac,DAC_CHANNEL_1);
HAL_DAC_SetValue(&hdac,DAC_CHANNEL_1,DAC_ALIGN_8B_R,0);
// AUDIO PA volume zero
#endif
HAL_DAC_Start(&hdac,DAC_CHANNEL_2);
HAL_DAC_SetValue(&hdac,DAC_CHANNEL_2,DAC_ALIGN_8B_R,0);
}
static void Board_Adc_Init(void)
{
// ADC init for Input Voltage readings
HAL_ADC_Start(&hadc1);
// ADC init for antenna forward power readings
HAL_ADC_Start(&hadc2);
// ADC init for antenna return power readings
HAL_ADC_Start(&hadc3);
}
static void Board_PowerDown_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStructure.Pin = POWER_DOWN;
HAL_GPIO_Init(POWER_DOWN_PIO, &GPIO_InitStructure);
// Set initial state - low to enable main regulator
GPIO_ResetBits(POWER_DOWN_PIO,POWER_DOWN);
}
// Band control GPIOs setup
//
// -------------------------------------------
// BAND BAND0 BAND1 BAND2
//
// 80m 1 1 x
// 40m 1 0 x
// 20/30m 0 0 x
// 15-10m 0 1 x
//
// -------------------------------------------
//
static void Board_BandCntr_Init(void)
{
#ifdef UI_BRD_MCHF
// FIXME: USE HAL Init here as well, this handles also the multiple Ports case
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Speed = GPIO_SPEED_LOW;
GPIO_InitStructure.Pin = BAND0|BAND1|BAND2;
HAL_GPIO_Init(BAND0_PIO, &GPIO_InitStructure);
#endif
// Set initial state - low (20m band)
GPIO_ResetBits(BAND0_PIO,BAND0);
GPIO_ResetBits(BAND1_PIO,BAND1);
// Pulse the latch relays line, active low, so set high to disable
GPIO_SetBits(BAND2_PIO,BAND2);
}
static void Board_Touchscreen_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
GPIO_InitStructure.Pull = GPIO_PULLUP;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStructure.Pin = TP_IRQ;
HAL_GPIO_Init(TP_IRQ_PIO, &GPIO_InitStructure);
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Pin = TP_CS;
HAL_GPIO_Init(TP_CS_PIO, &GPIO_InitStructure);
GPIO_SetBits(TP_CS_PIO, TP_CS);
}
/**
* Get us to a state where display and touch (and some other stuff) works, we have an idea about the
* rf hardware connected to us and then let the application do their thing
*/
void Board_InitMinimal()
{
// Enable clock on all ports
__GPIOA_CLK_ENABLE();
__GPIOB_CLK_ENABLE();
__GPIOC_CLK_ENABLE();
__GPIOD_CLK_ENABLE();
__GPIOE_CLK_ENABLE();
// LED init
Board_Led_Init();
Board_RedLed(LED_STATE_ON);
// Power up hardware
Board_PowerDown_Init();
// FROM HERE
// Filter control lines
Board_BandCntr_Init();
// Touchscreen SPI Control Signals Init
// TODO: Move to CubeMX Config
Board_Touchscreen_Init();
// Initialize LO
Osc_Init();
// TO HERE: Code be moved to init_full() if we figure out what causes the white screen @MiniTRX SPI
// TODO: It seems that some SPI display need some time to get started...
// LCD Init
UiLcdHy28_Init();
// we determine and set the correct RF board here
ts.rf_board = Si5351a_IsPresent()?FOUND_RF_BOARD_OVI40:FOUND_RF_BOARD_MCHF;
}
/*
* This initializes non-essential hardware for later use by the application
*/
void Board_InitFull()
{
#ifdef UI_BRD_MCHF
// on a STM32F4 MCHF UI we can have the internal RTC only if there is an SPI display.
if (ts.display->use_spi == true)
#endif
{
ts.rtc_present = Rtc_isEnabled();
}
#ifdef UI_BRD_MCHF
// we need to find out which keyboard layout before we init the GPIOs to use it.
// at this point we have to have called the display init and the rtc init
// in order to know which one to use.
if (ts.rtc_present)
{
Keypad_SetLayoutRTC_MCHF();
}
#endif
// Init keypad hw based on button map bm
Keypad_KeypadInit();
// Encoders init
UiRotaryFreqEncoderInit();
UiRotaryEncoderOneInit();
UiRotaryEncoderTwoInit();
UiRotaryEncoderThreeInit();
// Init DACs
Board_Dac_Init();
// Enable all ADCs
Board_Adc_Init();
}
/*
* @brief handle final power-off and delay
*/
void Board_HandlePowerDown() {
static ulong powerdown_delay = 0;
if(ts.powering_down) // are we powering down?
{
powerdown_delay++; // yes - do the powerdown delay
if(powerdown_delay > POWERDOWN_DELAY_COUNT) // is it time to power down
{
Board_Powerdown();
// never reached
}
}
}
/*
* @brief kills power hold immediately and waits for user to release power button
* @returns never returns
*/
void Board_Powerdown()
{
// we set this to input and add a pullup config
// this seems to be more reliably handling power down
// on F7 by rising the voltage to high enough levels.
// simply setting the OUTPUT to high did not do the trick here
// worked on F4, though.
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
GPIO_InitStructure.Pull = GPIO_PULLUP;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStructure.Pin = POWER_DOWN;
HAL_GPIO_Init(POWER_DOWN_PIO, &GPIO_InitStructure);
// disable all interrupts
__disable_irq();
// disable systick irq
HAL_SuspendTick();
// set clocks to default state
HAL_RCC_DeInit();
// clear Interrupt Enable Register & Interrupt Pending Register
const size_t icer_count = sizeof(NVIC->ICER)/sizeof(NVIC->ICER[0]);
for (size_t i = 0; i <icer_count; i++)
{
NVIC->ICER[i]=0xFFFFFFFF;
NVIC->ICPR[i]=0xFFFFFFFF;
}
Board_GreenLed(LED_STATE_OFF);
UiLcdHy28_BacklightEnable(false);
for(;;) { asm("nop"); }
// there is no coming back from here...
}
/**
* This is called once AFTER configuration data has been loaded from persistent storage
* (i.e. EEPROM or FLASH)
*/
void Board_PostInit(void)
{
// Set system tick interrupt
// Currently used for UI driver processing only
///mchf_board_set_system_tick_value();
// PTT control
Board_TxRxCntrPin_Init();
if (ts.rtc_present)
{
Rtc_SetPpm(ts.rtc_calib);
}
}
void Board_Reboot()
{
///Si570_ResetConfiguration(); // restore SI570 to factory default
*(__IO uint32_t*)(SRAM2_BASE) = 0x55;
__DSB();
#if defined(STM32F7) || defined(STM32H7)
SCB_CleanDCache();
#endif
NVIC_SystemReset(); // restart mcHF
}
// #pragma GCC optimize("O0")
static volatile bool busfault_detected;
#define TEST_ADDR_192 (0x20000000 + 0x0001FFFC)
#define TEST_ADDR_256 (0x20000000 + 0x0002FFFC)
#define TEST_ADDR_512 (0x20000000 + 0x0005FFFC)
// function below mostly based on http://stackoverflow.com/questions/23411824/determining-arm-cortex-m3-ram-size-at-run-time
__attribute__ ((naked)) void BusFault_Handler(void) {
/* NAKED function so we can be sure that SP is correct when we
* run our asm code below */
// DO NOT clear the busfault active flag - it causes a hard fault!
/* Instead, we must increase the value of the PC, so that when we
* return, we don't return to the same instruction.
*
* Registers are stacked as follows: r0,r1,r2,r3,r12,lr,pc,xPSR
* http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0337e/Babedgea.html
*
* So we want PC - the 6th down * 4 bytes = 24
*
* Then we add 2 - which IS DANGEROUS because we're assuming that the op
* is 2 bytes, but it COULD be 4.
*/
asm("mov r3, %0\n mov r2,#1\n str r2,[r3,#0]\n" : : "l" (&busfault_detected) );
// WE LEAVE 1 in busfault_detected -> if we have a busfault there is no memory here.
__asm__(
"ldr r0, [sp, #24]\n" // load the PC
"add r0, #2\n" // increase by 2 - dangerous, see above
"str r0, [sp, #24]\n" // save the PC back
"bx lr\n" // Return (function is naked so we must do this explicitly)
);
}
/*
* Tests if there is ram at the specified location
* Use with care and with 4byte aligned addresses.
* IT NEEDS A MATCHING BUSFAULT HANDLER!!!!
*/
__attribute__ ((noinline)) bool is_ram_at(volatile uint32_t* where) {
bool retval;
// we rely on the BusFault_Handler setting r4 to 0 (aka false) if a busfault occurs.
// this is truly bad code as it can be broken easily. The function cannot be optimize
// this this breaks the approach.
uint32_t oldval;
busfault_detected = false;
oldval = *where;
if (*where == oldval+1) {
*where = oldval;
}
retval = busfault_detected == false;
busfault_detected = false;
return retval;
}
uint32_t Board_RamSizeGet() {
uint32_t retval = 0;
// we enable the bus fault
// we now get bus faults if we access not available memory
// instead of hard faults
// this will run our very special bus fault handler in case no memory
// is at the defined location
#if defined(STM32F4) || defined(STM32F7)
SCB->SHCSR |= SCB_SHCSR_BUSFAULTENA_Msk;
if (is_ram_at((volatile uint32_t*)TEST_ADDR_512)){
retval=512;
} else if (is_ram_at((volatile uint32_t*)TEST_ADDR_256)){
retval=256;
} else if (is_ram_at((volatile uint32_t*)TEST_ADDR_192)){
retval=192;
}
// now we disable it
// we'll get hard faults as usual if we access wrong addresses
SCB->SHCSR &= ~SCB_SHCSR_BUSFAULTENA_Msk;
#elif defined(STM32H7)
// TODO make it detect the really available RAM
retval = 512;
#else
#warning Unkown processor, cannot determine ramsize
retval = 0;
#endif
return retval;
}
/**
* Determines the available RAM. Only supports 192 and 256 STM32F4 models
* Approach works but makes some assumptions. Do not change if you don't know
* what you are doing!
* USE WITH CARE!
*/
void Board_RamSizeDetection() {
// we enable the bus fault
// we now get bus faults if we access not available memory
// instead of hard faults
// this will run our very special bus fault handler in case no memory
// is at the defined location
ts.ramsize = Board_RamSizeGet();
}
static void Board_BandFilterPulseRelays(void)
{
// FIXME: Replace non_os_delay with HAL_Delay
GPIO_ResetBits(BAND2_PIO, BAND2);
// TODO: Check if we can go down to 10ms as per datasheet
// HAL_Delay(20);
non_os_delay();
GPIO_SetBits(BAND2_PIO, BAND2);
}
#ifdef SDR_AMBER
void Board_Amber_InputStateSet(uint8_t code)
{
uint8_t tstate = ts.amber_io8_state;
switch(code)
{
case 0: // PRE [11]
{
tstate = tstate | 3; // set bits
break;
}
case 1: // NoPRE [10]
{
tstate = tstate | 2; // set bit
tstate = tstate & ~1; // reset bit
break;
}
case 2: // Att12 [01]
{
tstate = tstate | 1; // set bit
tstate = tstate & ~2; // reset bit
break;
}
case 3: // ATT24 [00]
{
tstate = tstate & ~3; // reset bits
break;
}
default:
break;
}
if(ts.amber_io8_present)
{
ts.amber_io8_state = tstate;
Board_AmberIOx8_Write(tstate);
}
}
static void Board_Amber_BandFilterPulseRelays(bool up)
{
if(ts.amber_io8_present)
{
// Control, set relays bits to high
ts.amber_io8_state = ts.amber_io8_state | 48;
Board_AmberIOx8_Write(ts.amber_io8_state);
// Pulse
ts.amber_io8_state = ts.amber_io8_state & ~(up ? 16 : 32); // reset bit
Board_AmberIOx8_Write(ts.amber_io8_state);
// HAL_Delay(20);
non_os_delay();
// Off, set relays bits to high
ts.amber_io8_state = ts.amber_io8_state | 48;
Board_AmberIOx8_Write(ts.amber_io8_state);
}
}
static void Board_Amber_BPF_FilterSet(uint8_t code)
{
uint8_t tstate = ts.amber_io8_state;
switch(code)
{
case 0:
{
tstate = tstate & ~12; // reset bits
break;
}
case 1:
{
tstate = tstate | 4; // set bit
tstate = tstate & ~8; // reset bit
break;
}
case 2:
{
tstate = tstate & ~4; // reset bit
tstate = tstate | 8; // set bit
break;
}
case 3:
{
tstate = tstate | 12; // set bits
break;
}
default:
break;
}
if(ts.amber_io8_present)
{
ts.amber_io8_state = tstate;
Board_AmberIOx8_Write(tstate);
}
}
#endif
/**
* @brief switches one of the four LPF&BPF groups into the RX/TX signal path
* @param group 0: 80m, 1: 40m, 2: 20m , 3:10m
*/
void Board_SelectLpfBpf(uint8_t group)
{
// -------------------------------------------
// BAND BAND0 BAND1 BAND2
//
// 80m 1 1 x
// 40m 1 0 x
// 20/30m 0 0 x
// 15-10m 0 1 x
// ---------------------------------------------
// Amber
// ---------------------------------------------
// <=160m 1 1 x group==4 BPF-option - <160m
// Not in use 1 1 x group==5 BPF-option - 160m
// 6m 1 1 x group==6
// ---------------------------------------------
// Set LPFs:
// Set relays in groups, internal first, then external group
// state change via two pulses on BAND2 line, then idle
//
// then
//
// Set BPFs
// Constant line states for the BPF filter,
// always last - after LPF change
switch(group)
{
case 0:
{
#ifdef SDR_AMBER
// Subinternal group - Set(Up)
Board_Amber_BandFilterPulseRelays(true);
#endif
// Internal group - Set(High/Low)
GPIO_SetBits(BAND0_PIO, BAND0);
GPIO_ResetBits(BAND1_PIO, BAND1);
Board_BandFilterPulseRelays();
// External group -Set(High/High)
GPIO_SetBits(BAND0_PIO, BAND0);
GPIO_SetBits(BAND1_PIO, BAND1);
Board_BandFilterPulseRelays();
// BPF
GPIO_SetBits(BAND0_PIO, BAND0);
GPIO_SetBits(BAND1_PIO, BAND1);
#ifdef SDR_AMBER
Board_Amber_BPF_FilterSet(3);
#endif
break;
}
case 1:
{
#ifdef SDR_AMBER
// Subinternal group - Set(Up)
Board_Amber_BandFilterPulseRelays(true);
#endif
// Internal group - Set(High/Low)
GPIO_SetBits(BAND0_PIO, BAND0);
GPIO_ResetBits(BAND1_PIO, BAND1);
Board_BandFilterPulseRelays();
// External group - Reset(Low/High)
GPIO_ResetBits(BAND0_PIO, BAND0);
GPIO_SetBits(BAND1_PIO, BAND1);
Board_BandFilterPulseRelays();
// BPF
GPIO_SetBits(BAND0_PIO, BAND0);
GPIO_ResetBits(BAND1_PIO, BAND1);
#ifdef SDR_AMBER
Board_Amber_BPF_FilterSet(3);
#endif
break;
}
case 2:
{
#ifdef SDR_AMBER
// Subinternal group - Set(Up)
Board_Amber_BandFilterPulseRelays(true);
#endif
// Internal group - Reset(Low/Low)
GPIO_ResetBits(BAND0_PIO, BAND0);
GPIO_ResetBits(BAND1_PIO, BAND1);
Board_BandFilterPulseRelays();
// External group - Reset(Low/High)
GPIO_ResetBits(BAND0_PIO, BAND0);
GPIO_SetBits(BAND1_PIO, BAND1);
Board_BandFilterPulseRelays();
// BPF
GPIO_ResetBits(BAND0_PIO, BAND0);
GPIO_ResetBits(BAND1_PIO, BAND1);
#ifdef SDR_AMBER
Board_Amber_BPF_FilterSet(3);
#endif
break;
}
case 3:
{
#ifdef SDR_AMBER
// Subinternal group - Set(Up)
Board_Amber_BandFilterPulseRelays(true);
#endif
// Internal group - Reset(Low/Low)
GPIO_ResetBits(BAND0_PIO, BAND0);
GPIO_ResetBits(BAND1_PIO, BAND1);
Board_BandFilterPulseRelays();
// External group - Set(High/High)
GPIO_SetBits(BAND0_PIO, BAND0);
GPIO_SetBits(BAND1_PIO, BAND1);
Board_BandFilterPulseRelays();
// BPF
GPIO_ResetBits(BAND0_PIO, BAND0);
GPIO_SetBits(BAND1_PIO, BAND1);
#ifdef SDR_AMBER
Board_Amber_BPF_FilterSet(3);
#endif
break;
}
#ifdef SDR_AMBER
case 4: // <=160m (BPF-option - <160m)
{
// Subinternal group - Set(Down) - for 160m
Board_Amber_BandFilterPulseRelays(false);
// Internal group - Set(High/Low) - as 80m
GPIO_SetBits(BAND0_PIO, BAND0);
GPIO_ResetBits(BAND1_PIO, BAND1);
Board_BandFilterPulseRelays();
// External group -Set(High/High) - as 80m
GPIO_SetBits(BAND0_PIO, BAND0);
GPIO_SetBits(BAND1_PIO, BAND1);
Board_BandFilterPulseRelays();
// BPF - as 80m
GPIO_SetBits(BAND0_PIO, BAND0);
GPIO_SetBits(BAND1_PIO, BAND1);
Board_Amber_BPF_FilterSet(2);
break;
}
case 5: // Not in use (BPF-option - 160m)
{
// Subinternal group - Set(Down) - for 160m
Board_Amber_BandFilterPulseRelays(false);
// Internal group - Set(High/Low) - as 80m
GPIO_SetBits(BAND0_PIO, BAND0);
GPIO_ResetBits(BAND1_PIO, BAND1);
Board_BandFilterPulseRelays();
// External group -Set(High/High) - as 80m
GPIO_SetBits(BAND0_PIO, BAND0);
GPIO_SetBits(BAND1_PIO, BAND1);
Board_BandFilterPulseRelays();
// BPF - as 80m
GPIO_SetBits(BAND0_PIO, BAND0);
GPIO_SetBits(BAND1_PIO, BAND1);
Board_Amber_BPF_FilterSet(0);
break;
}
case 6: // 6m
{
// Subinternal group - Set(Down) - for 6m
Board_Amber_BandFilterPulseRelays(false);
// Internal group - Reset(Low/Low) - as 10m
GPIO_ResetBits(BAND0_PIO, BAND0);
GPIO_ResetBits(BAND1_PIO, BAND1);
Board_BandFilterPulseRelays();
// External group - Set(High/High) - as 10m
GPIO_SetBits(BAND0_PIO, BAND0);
GPIO_SetBits(BAND1_PIO, BAND1);
Board_BandFilterPulseRelays();
// BPF - as 80m
GPIO_SetBits(BAND0_PIO, BAND0);
GPIO_SetBits(BAND1_PIO, BAND1);
Board_Amber_BPF_FilterSet(1);
break;
}
#endif
default:
break;
}
}
const char* Board_BootloaderVersion()
{
const char* outs = "Unknown BL";
// We search for string "Version: " in bootloader memory
// this assume the bootloader starting at 0x8000000 and being followed by the virtual eeprom
// which starts at EEPROM_START_ADDRESS
for(uint8_t* begin = (uint8_t*)0x8000000; begin < (uint8_t*)EEPROM_START_ADDRESS-8; begin++)
{
if (memcmp("Version: ",begin,9) == 0)
{
outs = (const char*)&begin[9];
break;
}
else
{
if (memcmp("M0NKA 2",begin,7) == 0)
{
if (begin[11] == 0xb5)
{
outs = "M0NKA 0.0.0.9";
break;
}
else if (begin[11] == 0xd1)
{
outs = "M0NKA 0.0.0.14";
break;
}
}
}
}
return outs;
}
/**
* @brief set PA bias at the LM2931CDG (U18) using DAC Channel 2
*/
void Board_SetPaBiasValue(uint16_t bias)
{
// Set DAC Channel 1 DHR12L register
// DAC_SetChannel2Data(DAC_Align_8b_R,bias);
HAL_DAC_SetValue(&hdac, DAC_CHANNEL_2, DAC_ALIGN_8B_R, bias);
}
void Board_GreenLed(ledstate_t state)
{
switch(state)
{
case LED_STATE_ON:
GPIO_SetBits(GREEN_LED_PIO, GREEN_LED);
break;
case LED_STATE_OFF:
GPIO_ResetBits(GREEN_LED_PIO, GREEN_LED);
break;
default:
GPIO_ToggleBits(GREEN_LED_PIO, GREEN_LED);
break;
}
}
void Board_RedLed(ledstate_t state)
{
switch(state)
{
case LED_STATE_ON:
GPIO_SetBits(RED_LED_PIO, RED_LED);
break;
case LED_STATE_OFF:
GPIO_ResetBits(RED_LED_PIO, RED_LED);
break;
default:
GPIO_ToggleBits(RED_LED_PIO, RED_LED);
break;
}
}
#ifdef UI_BRD_OVI40
void Board_BlueLed(ledstate_t state)
{
switch(state)
{
case LED_STATE_ON:
GPIO_SetBits(BLUE_LED_PIO, BLUE_LED);
break;
case LED_STATE_OFF:
GPIO_ResetBits(BLUE_LED_PIO, BLUE_LED);
break;
default:
GPIO_ToggleBits(BLUE_LED_PIO, BLUE_LED);
break;
}
}
#endif
#ifdef SDR_AMBER
void Board_IllumButt()
{
if(ts.amber_io8_present)
{
// bit6 == 1 - Illumination of buttons is ON
if(ts.expflags2 & EXPFLAGS2_ILLUM_BUTT)
{
ts.amber_io8_state = ts.amber_io8_state | 64; // set bit
}
else
{
ts.amber_io8_state = ts.amber_io8_state & ~64; // reset bit
}
Board_AmberIOx8_Write(ts.amber_io8_state);
}
}
#endif
/**
* @brief sets the hw ptt line and by this switches the mcHF board signal path between rx and tx configuration
* @param tx_enable true == TX Paths, false == RX Paths
*/
void Board_EnableTXSignalPath(bool tx_enable)
{
// to make switching as noiseless as possible, make sure the codec lineout is muted/produces zero output before switching
if (tx_enable)
{
GPIO_SetBits(TXRX_CNTR_PIO,TXRX_CNTR); // TX on and switch CODEC audio paths
// Antenna Direction Output
// BPF Direction Output (U1,U2)
// PTT Optocoupler LED On (ACC Port) (U6)
// QSD Mixer Output Disable (U15)
// QSE Mixer Output Enable (U17)
// Codec LineIn comes from mcHF LineIn Socket (U3)
// Codec LineOut connected to QSE mixer (IQ Out) (U3a)
}
else
{
GPIO_ResetBits(TXRX_CNTR_PIO,TXRX_CNTR); // TX off
// Antenna Direction Input
// BPF Direction Input (U1,U2)
// PTT Optocoupler LED Off (ACC Port) (U6)
// QSD Mixer Output Enable (U15)
// QSE Mixer Output Disable (U17)
// Codec LineIn comes from RF Board QSD mixer (IQ In) (U3)
// Codec LineOut disconnected from QSE mixer (IQ Out) (U3a)
}
}
/**
* Is the hardware contact named DAH pressed
*/
bool Board_PttDahLinePressed() {
return !HAL_GPIO_ReadPin(PADDLE_DAH_PIO,PADDLE_DAH);
}
#ifdef SDR_AMBER_PTT_ALT
bool Board_PttAltLinePressed()
{
return !HAL_GPIO_ReadPin(BUTTON_S19_PIO, BUTTON_S19);
}
#endif
/**
* Is the hardware contact named DIT pressed
*/
bool Board_DitLinePressed() {
return !HAL_GPIO_ReadPin(PADDLE_DIT_PIO,PADDLE_DIT);
}
// For DACs MCP4725 - on band carrier TX depression
uint16_t LO_TX_SUPR_DAC_WriteReg(uint8_t qitem, uint16_t cal_value)
{
uint8_t write_address = qitem?LO_TX_SUPR_DAC1_WRITE:LO_TX_SUPR_DAC0_WRITE;
uint8_t write_data[2] =
{
(cal_value & 0x00FFFFFF) >> 4,
(cal_value & 0x000000FF) << 4
};
return UhsdrHw_I2C_WriteBlock(SERIALEEPROM_I2C, write_address, 64, 1, write_data, 2);
}
#ifdef SDR_AMBER
// For DAC MCP4725 - Alt. POWER level ctrl.
uint16_t ALT_RWP_CTRL_DAC_WriteReg(uint16_t vol_value)
{
uint8_t write_address = AMBER_DAC_PWR_TX_WRITE;
uint8_t write_data[2] =
{
(vol_value & 0x00FFFFFF) >> 4,
(vol_value & 0x000000FF) << 4
};
return UhsdrHw_I2C_WriteBlock(SI5351A_I2C, write_address, 64, 1, write_data, 2);
}
uint16_t Board_AmberIOx8_Write(uint8_t value)
{
uint8_t write_address = AMBER_IO8_WRITE;
uint8_t write_data[2] =
{
value,
value
};
return UhsdrHw_I2C_WriteBlock(SERIALEEPROM_I2C, write_address, 64, 1, write_data, 2);
}
uint16_t Board_AmberIOx4_Write(uint8_t command, uint8_t value)
{
return UhsdrHw_I2C_WriteRegister(SERIALEEPROM_I2C, AMBER_IO4_WRITE, command, 1, value);
}
uint16_t Board_AmberCS4270_Write(uint8_t map, uint8_t value)
{
return UhsdrHw_I2C_WriteRegister(CODEC_IQ_I2C, AMBER_CS4270_WRITE, map, 1, value);
}
#endif
// Determine the "band" for LO TX Supression
void LO_TX_SUPR_DAC_GetBand(uint32_t freq)
{
ts.band_lo_tx_supr = ts.band_index;
if (ts.band_lo_tx_supr == 16) // BAND_MODE_160
{
ts.band_lo_tx_supr = 10; // Don't calibrate add.OVI40 bands, but 160m is so far away...
}
if (ts.band_lo_tx_supr > 10)
{
ts.band_lo_tx_supr = 13; // Gen band for LO TX Supression. Don't calibrate add. OVI40 bands
}
if (ts.band_lo_tx_supr == 13) // CB band?
{
if (ts.HereIsEnableCB26Mc)
{
ts.band_lo_tx_supr = 11;
}
if (ts.HereIsEnableCB27Mc)
{
ts.band_lo_tx_supr = 12;
}
}
if (ts.band_lo_tx_supr == 8) // 28Mc?
{
if (freq > 28850000)
{
ts.band_lo_tx_supr = 14; // Additional adjustment point for the upper half of the band 28 Mc
}
}
if (ts.band_lo_tx_supr != ts.band_lo_tx_supr_old)
{
LO_TX_SUPR_DAC_WriteReg(0, ts.cal_lo_tx_supr0[ts.band_lo_tx_supr]); // Set the DAC0 voltage
LO_TX_SUPR_DAC_WriteReg(1, ts.cal_lo_tx_supr1[ts.band_lo_tx_supr]); // Set the DAC1 voltage
ts.band_lo_tx_supr_old = ts.band_lo_tx_supr;
}
}
void CW_Smooth_Settings(void)
{
switch (ts.cw_smooth)
{
case 0: // 3 ms
ts.cw_smooth_len = 1;
ts.cw_smooth_steps = 5;
break;
case 1: // 5 ms
ts.cw_smooth_len = 2;
ts.cw_smooth_steps = 9;
break;
case 2: // 8 ms
ts.cw_smooth_len = 3;
ts.cw_smooth_steps = 13;
break;
case 3: // 10 ms
ts.cw_smooth_len = 4;
ts.cw_smooth_steps = 18;
break;
}
}
#ifndef UI_BRD_MCHF
void MIC_bias_set(void)
{
if(ts.tx_mic_boost > 0) // dinamic MIC
{
GPIO_SetBits(AUDIO_MIC_BIAS_PIO,AUDIO_MIC_BIAS);
}
else // electret MIC
{
GPIO_ResetBits(AUDIO_MIC_BIAS_PIO,AUDIO_MIC_BIAS);
}
}
#endif