diff options
Diffstat (limited to 'firmware/src')
| -rw-r--r-- | firmware/src/bsp.c | 38 | ||||
| -rw-r--r-- | firmware/src/cmd.c | 27 | ||||
| -rw-r--r-- | firmware/src/driver/f1956.c | 61 | ||||
| -rw-r--r-- | firmware/src/driver/mcp3202.c | 129 | ||||
| -rw-r--r-- | firmware/src/driver/mcp4725.c | 101 | ||||
| -rw-r--r-- | firmware/src/driver/si4468.c | 201 | ||||
| -rw-r--r-- | firmware/src/main.c | 343 | ||||
| -rw-r--r-- | firmware/src/module/drain.c | 123 | ||||
| -rw-r--r-- | firmware/src/module/gate.c | 78 | ||||
| -rw-r--r-- | firmware/src/peripheral/ring.c | 115 | ||||
| -rw-r--r-- | firmware/src/peripheral/spi.c | 106 | ||||
| -rw-r--r-- | firmware/src/peripheral/timer.c | 57 | ||||
| -rw-r--r-- | firmware/src/peripheral/twi.c | 114 | ||||
| -rw-r--r-- | firmware/src/peripheral/uart.c | 239 | ||||
| -rw-r--r-- | firmware/src/status.c | 46 | ||||
| -rw-r--r-- | firmware/src/util.c | 152 |
16 files changed, 1930 insertions, 0 deletions
diff --git a/firmware/src/bsp.c b/firmware/src/bsp.c new file mode 100644 index 0000000..fdb8a39 --- /dev/null +++ b/firmware/src/bsp.c @@ -0,0 +1,38 @@ +/** + * + * Author: Dylan Muller + * Copyright (c) 2025 + * All rights reserved. + * + * - Commercial/IP use prohibited. + * - Attribution required. + * See License.txt + * + */ + +#include "setup.h" +#include "bsp.h" + +#include <stdlib.h> +#include <stdio.h> +#include <avr/io.h> +#include <util/delay.h> +#include <avr/interrupt.h> +#include <avr/wdt.h> + +void bsp_soft_reset(void) +{ + cli(); + + MCUSR &= ~(1 << WDRF); + + WDTCSR = (1 << WDCE) | (1 << WDE); + WDTCSR = (1 << WDP1); + WDTCSR = WDTCSR | 0x40; + + MCUSR &= ~(1 << WDRF); + + sei(); + + while(1){}; +} diff --git a/firmware/src/cmd.c b/firmware/src/cmd.c new file mode 100644 index 0000000..f55ed80 --- /dev/null +++ b/firmware/src/cmd.c @@ -0,0 +1,27 @@ +/** + * + * Author: Dylan Muller + * Copyright (c) 2025 + * All rights reserved. + * + * - Commercial/IP use prohibited. + * - Attribution required. + * See License.txt + * + */ + +#include "cmd.h" + +#include <avr/pgmspace.h> + +const char* const cmd_lut[CMD_SIZE][CMD_ENTRY_SIZE] PROGMEM = { + {CMD_POWER_GET_STR, CMD_POWER_GET_DESC}, + {CMD_POWER_SET_STR, CMD_POWER_SET_DESC}, + {CMD_TX_STR, CMD_TX_DESC}, + {CMD_RX_STR, CMD_RX_DESC}, + {CMD_DRAIN_SET_STR, CMD_DRAIN_SET_DESC}, + {CMD_PROBE_STR, CMD_PROBE_DESC}, + {CMD_RESET_STR, CMD_RESET_DESC}, + {CMD_REPORT_STR, CMD_REPORT_DESC}, + {CMD_HELP_STR, CMD_HELP_DESC} + }; diff --git a/firmware/src/driver/f1956.c b/firmware/src/driver/f1956.c new file mode 100644 index 0000000..3af5fba --- /dev/null +++ b/firmware/src/driver/f1956.c @@ -0,0 +1,61 @@ +/** + * + * Author: Dylan Muller + * Copyright (c) 2025 + * All rights reserved. + * + * - Commercial/IP use prohibited. + * - Attribution required. + * See License.txt + * + */ + +#include "setup.h" + +#include "peripheral/spi.h" +#include "driver/f1956.h" + +#include <avr/io.h> +#include <avr/interrupt.h> +#include <util/delay.h> + +void att_init(uint8_t init_value) +{ + F1956_DEV_0_DDR |= (1 << F1956_DEV_0_SS); + F1956_DEV_0_PORT |= (1 << F1956_DEV_0_SS); + + att_set(F1956_ID_INPUT_ATT, init_value); + _delay_ms(10); +} + +void att_set( + uint8_t id, + uint8_t value +) +{ + uint8_t dev_id = 0x0; + uint8_t dev_ss = 0x0; + uint8_t* dev_port = 0x0; + uint8_t cmd_buf[2] = {0x0, 0x0}; + + switch (id) + { + case F1956_ID_INPUT_ATT: + dev_id = F1956_DEV_INPUT_ATT; + break; + } + + switch (dev_id) + { + case F1956_DEV_INPUT_ATT: + dev_ss = F1956_DEV_0_SS; + dev_port = (uint8_t*)&F1956_DEV_0_PORT; + break; + } + + cmd_buf[0] = value & 0x7F; + cmd_buf[1] = F1956_DEV_INPUT_ATT_ADDR; + + spi_start(cmd_buf, cmd_buf, sizeof(cmd_buf), dev_port, dev_ss); + spi_flush(); +} diff --git a/firmware/src/driver/mcp3202.c b/firmware/src/driver/mcp3202.c new file mode 100644 index 0000000..07f84db --- /dev/null +++ b/firmware/src/driver/mcp3202.c @@ -0,0 +1,129 @@ +/** + * + * Author: Dylan Muller + * Copyright (c) 2025 + * All rights reserved. + * + * - Commercial/IP use prohibited. + * - Attribution required. + * See License.txt + * + */ + +#include "setup.h" + +#include "peripheral/spi.h" +#include "driver/mcp3202.h" + +#include <avr/io.h> +#include <avr/interrupt.h> +#include <util/delay.h> + +void adc_init(void) +{ + MCP3202_DEV_0_DDR |= (1 << MCP3202_DEV_0_SS); + MCP3202_DEV_1_DDR |= (1 << MCP3202_DEV_1_SS); + + MCP3202_DEV_0_PORT &= ~(1 << MCP3202_DEV_0_SS); + MCP3202_DEV_1_PORT &= ~(1 << MCP3202_DEV_1_SS); + + _delay_ms(TRANSACT_DELAY_MS); + + MCP3202_DEV_0_PORT |= (1 << MCP3202_DEV_0_SS); + MCP3202_DEV_1_PORT |= (1 << MCP3202_DEV_1_SS); + + _delay_ms(TRANSACT_DELAY_MS); +} + +uint16_t adc_read( + uint8_t id, + uint8_t conv_bypass +) +{ + uint8_t dev_id = 0x0; + uint8_t dev_ch = 0x0; + uint8_t dev_ss = 0x0; + uint8_t* dev_port = 0x0; + uint8_t cmd_buf[3] = {0x0, 0x0, 0x0}; + uint16_t adc_daq = 0x0; + + switch(id) + { + case MCP3202_ID_FW_POWER: + dev_id = MCP3202_DEV_FW_POWER; + dev_ch = MCP3202_CHN_FW_POWER; + break; + + case MCP3202_ID_REV_POWER: + dev_id = MCP3202_DEV_REV_POWER; + dev_ch = MCP3202_CHN_REV_POWER; + break; + + case MCP3202_ID_DRAIN_VOLT: + dev_id = MCP3202_DEV_DRAIN_VOLT; + dev_ch = MCP3202_CHN_DRAIN_VOLT; + break; + + case MCP3202_ID_DRAIN_AMP: + dev_id = MCP3202_DEV_DRAIN_AMP; + dev_ch = MCP3202_CHN_DRAIN_AMP; + break; + } + + switch(dev_id) + { + case MCP3202_DEV_0: + dev_port = (uint8_t*)&MCP3202_DEV_0_PORT; + dev_ss = MCP3202_DEV_0_SS; + break; + + case MCP3202_DEV_1: + dev_port = (uint8_t*)&MCP3202_DEV_1_PORT; + dev_ss = MCP3202_DEV_1_SS; + break; + } + + cmd_buf[0] = MCP3202_REQ_START; + cmd_buf[1] = (dev_ch == MCP3202_CHN_0) ? MCP3202_REQ_CH0 : MCP3202_REQ_CH1; + cmd_buf[2] = MCP3202_REQ_PAD; + + spi_start(cmd_buf, cmd_buf, sizeof(cmd_buf), dev_port, dev_ss); + spi_flush(); + + adc_daq = (uint16_t)(cmd_buf[MCP3202_DAQ_MSB]); + adc_daq |= ((uint16_t)(cmd_buf[MCP3202_DAQ_LSB] & 0xF) << 8); + adc_daq = adc_daq & 0xFFF; + + if (conv_bypass == 0) + { + switch(id) + { + case MCP3202_ID_DRAIN_VOLT: + adc_daq = (uint32_t)(((uint32_t)adc_daq * MCP3202_DRAIN_VOLT_GAIN)/10); + break; + case MCP3202_ID_DRAIN_AMP: + adc_daq = (uint32_t)(((uint32_t)adc_daq * 10)/MCP3202_DRAIN_AMP_GAIN); + break; + } + } + + return (uint16_t)adc_daq; +} + +uint16_t adc_read_n( + uint8_t id, + uint8_t n +) +{ + uint32_t x = 0; + uint8_t i = 0; + + for (i = 0; i < n; i++) + { + x += adc_read(id, 0); + } + + x /= n; + + return (uint16_t)x; +} diff --git a/firmware/src/driver/mcp4725.c b/firmware/src/driver/mcp4725.c new file mode 100644 index 0000000..4cb9bd6 --- /dev/null +++ b/firmware/src/driver/mcp4725.c @@ -0,0 +1,101 @@ +/** + * + * Author: Dylan Muller + * Copyright (c) 2025 + * All rights reserved. + * + * - Commercial/IP use prohibited. + * - Attribution required. + * See License.txt + * + */ + +#include "setup.h" + +#include "peripheral/twi.h" +#include "driver/mcp4725.h" + +#include <avr/io.h> +#include <avr/interrupt.h> +#include <util/delay.h> + +void dac_init(uint16_t init_value) +{ + dac_write(MCP4725_ID_GATE_VOLT, init_value, 0); + dac_write(MCP4725_ID_DRAIN_VOLT, init_value, 0); + + _delay_ms(TRANSACT_DELAY_MS); +} + +void dac_write( + uint8_t id, + uint16_t value, + uint8_t conv_bypass +) +{ + uint8_t dev_addr = 0x0; + uint8_t cmd_buf[2] = {0x0, 0x0}; + uint8_t drain_mode = 0x0; + + switch(id) + { + case MCP4725_ID_GATE_VOLT: + dev_addr = TWI_ADDRESS_W(MCP4725_DEV_GATE_VOLT_ADDR); + break; + case MCP4725_ID_DRAIN_VOLT: + dev_addr = TWI_ADDRESS_W(MCP4725_DEV_DRAIN_VOLT_ADDR); + drain_mode = 1; + break; + } + + if (conv_bypass == 0) + { + if (drain_mode == 1) + { + value = (uint16_t)(((uint32_t)value * 10)/MCP4725_DRAIN_GAIN); + } + } + + cmd_buf[0] = ((value >> 8) & 0xF) | MCP4725_BIT_PD0(0) | MCP4725_BIT_PD1(0); + cmd_buf[1] = value & 0xFF; + + twi_start(dev_addr, cmd_buf, sizeof(cmd_buf)); + twi_flush(); +} + +uint16_t dac_read( + uint8_t id, + uint8_t conv_bypass +) +{ + uint8_t dev_addr = 0x0; + uint8_t cmd_buf[3] = {0x0, 0x0, 0x0}; + uint16_t value = 0x0; + uint8_t drain_mode = 0x0; + + switch(id) + { + case MCP4725_ID_GATE_VOLT: + dev_addr = TWI_ADDRESS_R(MCP4725_DEV_GATE_VOLT_ADDR); + break; + case MCP4725_ID_DRAIN_VOLT: + dev_addr = TWI_ADDRESS_R(MCP4725_DEV_DRAIN_VOLT_ADDR); + drain_mode = 1; + break; + } + + twi_start(dev_addr, cmd_buf, sizeof(cmd_buf)); + twi_flush(); + + value = (((uint16_t)cmd_buf[1]) << 4) | (cmd_buf[2] >> 4); + + if (conv_bypass == 0) + { + if (drain_mode) + { + value = (uint16_t)(((uint32_t)value * MCP4725_DRAIN_GAIN)/10); + } + } + + return (value & 0xFFF); +} diff --git a/firmware/src/driver/si4468.c b/firmware/src/driver/si4468.c new file mode 100644 index 0000000..17dd77d --- /dev/null +++ b/firmware/src/driver/si4468.c @@ -0,0 +1,201 @@ +/** + * + * Author: Dylan Muller + * Copyright (c) 2025 + * All rights reserved. + * + * - Commercial/IP use prohibited. + * - Attribution required. + * See License.txt + * + */ + +#include "setup.h" + +#include "peripheral/spi.h" + +#include "driver/si4468.h" +#include "driver/radio_config.h" +#include "driver/device.h" + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include <avr/io.h> +#include <avr/interrupt.h> +#include <util/delay.h> +#include <util/atomic.h> +#include <avr/pgmspace.h> + + +static const uint8_t config[] PROGMEM = RADIO_CONFIGURATION_DATA_ARRAY; + +void si4468_init(void) +{ + SI4468_DEV_0_DDR |= (1 << SI4468_DEV_0_SS); + SI4468_DEV_0_PORT |= (1 << SI4468_DEV_0_SS); +} + +void si44468_get_device( + int id, + t_dev* device +) +{ + uint8_t dev_id = 0x0; + uint8_t dev_ss = 0x0; + uint8_t* dev_port = 0x0; + + device->dev_id = 0; + device->dev_ss = 0; + device->dev_port = 0; + + switch (id) + { + case SI4468_ID_RADIO: + dev_id = SI4468_DEV_RADIO; + break; + } + + switch (dev_id) + { + case SI4468_DEV_RADIO: + dev_ss = SI4468_DEV_0_SS; + dev_port = (uint8_t*)&SI4468_DEV_0_PORT; + break; + } + + device->dev_id = dev_id; + device->dev_ss = dev_ss; + device->dev_port = dev_port; +} + +static uint8_t si4468_get_response( + void* buf, + uint8_t len +) +{ + t_dev device; + uint8_t cts = 0x0; + uint8_t* cmd_buf = 0x0; + uint16_t cmd_size = 0x0; + uint8_t i = 0; + + si44468_get_device(SI4468_ID_RADIO, &device); + + cmd_size = len + SI4468_GET_RESP_LEN; + cmd_buf = (uint8_t*)malloc((cmd_size)); + + cmd_buf[0] = SI4468_CMD_READ_CMD_BUFF; + cmd_buf[1] = SI4468_CTS; + + spi_start(cmd_buf, cmd_buf, SI4468_GET_RESP_LEN, device.dev_port, device.dev_ss); + spi_flush(); + + cts = (cmd_buf[0] == SI4468_CTS); + + if(cts) + { + if (len > 0) + { + for(i = 0x0; i < cmd_size; i++) + { + cmd_buf[i] = SI4468_CTS; + } + + cmd_buf[0] = SI4468_CMD_READ_CMD_BUFF; + + spi_start(cmd_buf, cmd_buf, cmd_size, (uint8_t*)&SI4468_DEV_0_PORT, SI4468_DEV_0_SS); + spi_flush(); + + for(i = SI4468_GET_RESP_LEN; i < cmd_size; i++) + { + ((uint8_t*)buf)[i - SI4468_GET_RESP_LEN] = cmd_buf[i]; + } + + } + } + + free(cmd_buf); + + return cts; +} + +static uint8_t si4468_wait_for_response( + void* buf, + uint8_t buf_len +) +{ + while(!si4468_get_response(buf, buf_len)) + { + _delay_ms(10); + } + + return 1; +} + +static void si4468_exec_api( + void* in, + size_t in_len, + void* out, + uint8_t out_len +) +{ + t_dev device; + si44468_get_device(SI4468_ID_RADIO, &device); + + if (si4468_wait_for_response(0, 0)) + { + spi_start(in, 0, in_len, device.dev_port, device.dev_ss); + spi_flush(); + + if (out != 0) + { + si4468_wait_for_response(out, out_len); + } + } +} + +void si4468_apply_startup_config(void) +{ + uint16_t i = 0; + uint8_t buf[100]; + + for(i = 0; i < sizeof(config); i++) + { + memcpy_P(buf, &config[i], sizeof(buf)); + uint8_t tmp = buf[0]; + si4468_exec_api(&buf[1], tmp, 0, 0); + i += tmp; + } +} + +void si4468_get_info( + si4468_info_t* info +) +{ + uint8_t data[8] = { + SI4468_CMD_PART_INFO + }; + + si4468_exec_api(data, 1, data, 8); + + info->chipRev = data[0]; + info->part = (data[1]<<8) | data[2]; + info->partBuild = data[3]; + info->id = (data[4]<<8) | data[5]; + info->customer = data[6]; + info->romId = data[7]; +} + +void si4468_tx_mode(void) +{ + uint8_t packet[] = {SI4468_CMD_START_TX, 0,0,0, 0,0,0 }; + si4468_exec_api(packet, sizeof(packet), 0, 0); +} + +void si4468_rx_mode(void) +{ + uint8_t packet[] = {SI4468_CMD_CHANGE_STATE, 0x3}; + si4468_exec_api(packet, sizeof(packet), 0, 0); +} diff --git a/firmware/src/main.c b/firmware/src/main.c new file mode 100644 index 0000000..02d103d --- /dev/null +++ b/firmware/src/main.c @@ -0,0 +1,343 @@ +/**
+ *
+ * Author: Dylan Muller
+ * Copyright (c) 2025
+ * All rights reserved.
+ *
+ * - Commercial/IP use prohibited.
+ * - Attribution required.
+ * See License.txt
+ *
+ */
+
+#include "setup.h"
+
+#include "peripheral/timer.h"
+#include "peripheral/uart.h"
+#include "peripheral/twi.h"
+#include "peripheral/spi.h"
+
+#include "driver/mcp3202.h"
+#include "driver/mcp4725.h"
+#include "driver/f1956.h"
+#include "driver/si4468.h"
+
+#include "module/gate.h"
+#include "module/drain.h"
+
+#include "cmd.h"
+#include "bsp.h"
+#include "status.h"
+#include "util.h"
+
+#include <avr/io.h>
+#include <util/delay.h>
+#include <avr/interrupt.h>
+#include <avr/pgmspace.h>
+#include <avr/wdt.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+extern t_gate_status gate_status;
+extern t_drain_status drain_status;
+extern t_system_status system_status;
+
+void system_info(void)
+{
+ CMD_PRINT("SYSTEM BOOT v" SYS_VER_STR);
+ CMD_PRINT("SYSTEM INIT");
+ printf("%d BYTES AVAILABLE\n", util_get_free_mem());
+}
+
+void setup(void)
+{
+ cli();
+
+ gate_status.status = GATE_STATUS_INACTIVE;
+ drain_status.status = DRAIN_STATUS_INACTIVE;
+ system_status.status = SYSTEM_STATUS_IDLE;
+
+ wdt_reset();
+ wdt_disable();
+
+ timer_init();
+
+ uart_init();
+ twi_init();
+ spi_init();
+
+ sei();
+
+ _delay_ms(INIT_DELAY_MS);
+
+ si4468_init();
+
+ adc_init();
+ att_init(0);
+ dac_init(0);
+
+ _delay_ms(INIT_DELAY_MS);
+
+ si4468_apply_startup_config();
+
+ _delay_ms(INIT_DELAY_MS);
+
+ si4468_rx_mode();
+}
+
+void report_system_status()
+{
+ uint16_t gate_v_set = 0x0;
+ uint16_t gate_v_count = 0x0;
+
+ uint16_t drain_v_set = 0x0;
+ uint16_t drain_v_adc = 0x0;
+ uint16_t drain_amp_count = 0x0;
+ uint16_t drain_amp = 0x0;
+ uint16_t drain_v_count = 0x0;
+
+ uint16_t fw_pwr_adc = 0x0;
+ uint16_t fw_pwr_volt = 0x0;
+ int16_t fw_pwr = 0x0;
+
+ // gate status
+
+ gate_status.q_drain = GATE_PROBE_DRAIN;
+ gate_v_count = dac_read(MCP4725_ID_GATE_VOLT, 0);
+ util_count_to_volt(gate_v_count, &gate_v_set);
+ gate_status.v_set = gate_v_set;
+
+
+ printf_P(PSTR("- GATE -\n"));
+ printf_P(PSTR("STATUS : %s\n"),
+ pgm_read_word(&gate_lut[gate_status.status]));
+ printf_P(PSTR("Q-VOLT (mV) : %d\n"), gate_status.q_volt);
+ printf_P(PSTR("Q-AMP (mA) : %d\n"), gate_status.q_amp);
+ printf_P(PSTR("Q-TIME (ms) : %d\n"), gate_status.q_time);
+ printf_P(PSTR("V-SET (mV) : %d\n"), gate_status.v_set);
+ printf_P(PSTR("Q-DRAIN (mV) : %d\n"), gate_status.q_drain);
+
+ // drain status
+ drain_v_count = dac_read(MCP4725_ID_DRAIN_VOLT, 0);
+ util_count_to_volt(drain_v_count, &drain_v_set);
+ drain_status.v_set = drain_v_set;
+
+ drain_v_count = adc_read(MCP3202_ID_DRAIN_VOLT, 0);
+ util_count_to_volt(drain_v_count, &drain_v_adc);
+ drain_status.v_adc = drain_v_adc;
+
+ drain_amp_count = adc_read(MCP3202_ID_DRAIN_AMP, 0);
+ util_count_to_volt(drain_amp_count, &drain_amp);
+ drain_status.amp = drain_amp;
+
+ printf_P(PSTR("- DRAIN -\n"));
+ printf_P(PSTR("STATUS : %s\n"),
+ pgm_read_word(&drain_lut[drain_status.status]));
+ printf_P(PSTR("V-ADC (mV) : %d\n"), drain_status.v_adc);
+ printf_P(PSTR("V-SET (mV) : %d\n"), drain_status.v_set);
+ printf_P(PSTR("AMP (mA) : %d\n"), drain_status.amp);
+
+ // system status
+ fw_pwr_adc = adc_read_n(MCP3202_ID_FW_POWER, DRAIN_POWER_SAMPLES);
+ util_count_to_volt(fw_pwr_adc, &fw_pwr_volt);
+ util_count_to_pwr(fw_pwr_adc, &fw_pwr);
+ system_status.v_fw = fw_pwr_volt;
+ system_status.p_fw = fw_pwr;
+
+ printf_P(PSTR("- SYSTEM -\n"));
+ printf_P(PSTR("STATUS : %s\n"),
+ pgm_read_word(&system_lut[system_status.status]));
+ printf_P(PSTR("V-FWD (mV) : %d\n"), system_status.v_fw);
+ printf_P(PSTR("P-FWD (cdBm) : %d\n"), system_status.p_fw);
+ printf_P(PSTR("P-SET (cdBm) : %d\n"), system_status.p_set);
+ printf_P(PSTR("T-DELTA (ms) : %d\n"), system_status.d_time);
+}
+
+void loop()
+{
+ char uart_buffer[UART_BUF_SIZE];
+ char cmd_buffer[UART_BUF_SIZE];
+ char* cmd_tok = 0x0;
+ int16_t argv[CMD_MAX_ARGS];
+ uint16_t argc = 0x0;
+ bool gate_probed = false;
+
+ while (1)
+ {
+ cmd_tok = 0x0;
+ argc = 0x0;
+
+ if(uart_ngets(uart_buffer, UART_BUF_SIZE))
+ {
+ cmd_tok = strtok(uart_buffer, " ");
+ strcpy(cmd_buffer, cmd_tok);
+
+ while ((cmd_tok = strtok(NULL, " ")) != NULL) {
+ argv[argc++] = atoi(cmd_tok);
+ }
+
+ if (CMD_SWITCH(CMD_REPORT, uart_buffer))
+ {
+ CMD_PRINT("DUMP SYSTEM REPORT");
+ report_system_status();
+ }
+
+ else if (CMD_SWITCH(CMD_POWER_GET, uart_buffer))
+ {
+ CMD_PRINT("DUMP POWER ADC");
+ uint16_t fw_pwr_adc = adc_read_n(MCP3202_ID_FW_POWER,10);
+ CMD_PRINT_FMT("%d", fw_pwr_adc);
+ }
+
+ else if (CMD_SWITCH(CMD_PROBE, uart_buffer))
+ {
+ uint16_t gate_probe_count = 0x0;
+ uint16_t drain_current = 0x0;
+ uint32_t time_start = 0x0;
+ uint16_t time_delta = 0x0;
+
+ if (!gate_probed)
+ {
+ CMD_PRINT("STARTING Q-PROBE");
+ CMD_PRINT_FMT("DRAIN_Q_AMP = %d mA", DRAIN_Q_AMP);
+
+ time_start = timer_millis();
+
+ si4468_rx_mode();
+ drain_current = gate_probe(DRAIN_Q_AMP, &gate_probe_count);
+
+ time_delta = (uint16_t)(timer_millis() - time_start);
+ gate_status.q_time = time_delta;
+
+ CMD_PRINT_FMT("PROBE COMPLETED IN %d ms", time_delta);
+
+ if (drain_current >= DRAIN_Q_AMP)
+ {
+ gate_status.status = GATE_STATUS_PROBE_SUCCESS;
+ CMD_PRINT_FMT("Q-PROBE SUCCEEDED, IQ = %d mA", drain_current);
+ gate_probed = true;
+ }
+ else
+ {
+ gate_status.status = GATE_STATUS_PROBE_ERROR;
+ CMD_PRINT_FMT("Q-PROBE FAILED, IQ = %d mA", drain_current);
+ continue;
+ }
+ }
+ else
+ {
+ CMD_ERROR("ACTIVE GATE PROBE");
+ }
+ }
+
+ else if (CMD_SWITCH(CMD_POWER_SET, cmd_buffer))
+ {
+ if (argc != 1)
+ {
+ CMD_ERROR("USAGE: POWER <cdBm>");
+ continue;
+ }
+
+ int16_t rf_power = argv[0];
+
+ if (rf_power < DRAIN_POWER_LOW_LIM ||
+ rf_power > DRAIN_POWER_HIGH_LIM ||
+ (rf_power % DRAIN_POWER_MODULUS) != 0)
+ {
+ CMD_ERROR("INVALID INPUT POWER");
+ continue;
+ }
+
+ if (!gate_probed)
+ {
+ CMD_PRINT("NO GATE PROBE");
+ continue;
+
+ }
+
+ system_status.p_set = rf_power;
+
+ CMD_PRINT_FMT("POWER SETPOINT = %d cdBm", rf_power);
+ CMD_PRINT("STARTING LINEAR SWEEP");
+ drain_sweep_pwr(rf_power);
+ CMD_PRINT_FMT("SWEEP COMPLETED IN %d ms", system_status.d_time);
+ }
+
+ else if (CMD_SWITCH(CMD_DRAIN_SET, cmd_buffer))
+ {
+ if (argc != 1)
+ {
+ CMD_ERROR("USAGE: DRAIN <count>");
+ continue;
+ }
+
+ uint16_t drain_count = argv[0];
+ if (drain_count > 4096)
+ {
+ CMD_ERROR("RANGE ERROR");
+ continue;
+ }
+
+ dac_write(MCP4725_ID_DRAIN_VOLT, drain_count, 1);
+ }
+
+ else if (CMD_SWITCH(CMD_RX, uart_buffer))
+ {
+ CMD_PRINT("RADIO RX MODE");
+ si4468_rx_mode();
+ }
+
+ else if (CMD_SWITCH(CMD_TX, uart_buffer))
+ {
+ CMD_PRINT("RADIO TX MODE");
+ si4468_tx_mode();
+ }
+
+ else if (CMD_SWITCH(CMD_HELP, uart_buffer))
+ {
+ uint16_t i = 0x0;
+
+ CMD_PRINT("DUMP COMMANDS");
+ printf("- COMMANDS -\n");
+ for(i = 0; i < CMD_SIZE; i++)
+ {
+ printf_P(PSTR("%-10s - %-10s\n"),
+ pgm_read_word(&cmd_lut[i][0]),
+ pgm_read_word(&cmd_lut[i][1]));
+ }
+ }
+
+ else if (CMD_SWITCH(CMD_LINK_REPLY, uart_buffer))
+ {
+ CMD_PRINT("HELLO");
+ }
+
+ else if (CMD_SWITCH(CMD_RESET, uart_buffer))
+ {
+ CMD_PRINT("SOFT RESET");
+ _delay_ms(10);
+
+ bsp_soft_reset();
+ }
+
+ else
+ {
+ CMD_PRINT("UNKNOWN COMMAND");
+ }
+ }
+ }
+}
+
+int main()
+{
+ setup();
+ system_info();
+
+ loop();
+
+ return 0;
+}
+
+
diff --git a/firmware/src/module/drain.c b/firmware/src/module/drain.c new file mode 100644 index 0000000..1cb6110 --- /dev/null +++ b/firmware/src/module/drain.c @@ -0,0 +1,123 @@ +/** + * + * Author: Dylan Muller + * Copyright (c) 2025 + * All rights reserved. + * + * - Commercial/IP use prohibited. + * - Attribution required. + * See License.txt + * + */ + +#include "setup.h" + +#include "peripheral/timer.h" + +#include "driver/mcp3202.h" +#include "driver/mcp4725.h" +#include "driver/si4468.h" + +#include "module/gate.h" +#include "module/drain.h" + +#include "util.h" +#include "status.h" + +#include <stdlib.h> +#include <stdio.h> +#include <avr/interrupt.h> +#include <util/delay.h> + +extern t_drain_status drain_status; +extern t_system_status system_status; + +uint16_t drain_sweep_pwr( + int16_t target_fw_pwr +) +{ + uint16_t drain_count = 0x0; + uint16_t drain_inc = 0x0; + uint16_t fw_pwr_adc = 0x0; + int16_t fw_pwr = 0x0; + uint16_t test_count = 0x0; + int16_t test_boundary = DRAIN_TEST_BOUNDARY; + + system_status.status = SYSTEM_STATUS_BUSY; + drain_status.status = DRAIN_STATUS_POWER_CONTROL; + + drain_count = dac_read(MCP4725_ID_DRAIN_VOLT, 1); + _delay_ms(TRANSACT_DELAY_MS); + + si4468_tx_mode(); + _delay_ms(TRANSACT_DELAY_MS); + + dac_write(MCP4725_ID_DRAIN_VOLT, drain_count, 1); + _delay_ms(TRANSACT_DELAY_MS); + + fw_pwr_adc = adc_read_n(MCP3202_ID_FW_POWER, 10); + util_count_to_pwr(fw_pwr_adc, &fw_pwr); + + _delay_ms(TRANSACT_DELAY_MS); + + timer_reset(); + uint32_t time_start = timer_millis(); + bool timeout = false; + + while ((test_count < DRAIN_TEST_COUNT) && + (drain_count < DRAIN_PROBE_LIMIT)) + { + if ((timer_millis() - time_start) > DRAIN_SETPOINT_TIMEOUT_MS) + { + timeout = true; + break; + } + + if (abs(fw_pwr - target_fw_pwr) > DRAIN_PROBE_INC_THRESHOLD) + { + drain_inc = DRAIN_PROBE_INC_COARSE; + } + else + { + drain_inc = DRAIN_PROBE_INC_FINE; + } + + if (fw_pwr < (target_fw_pwr)) + { + drain_count += drain_inc; + } + else if (fw_pwr > (target_fw_pwr)) + { + drain_count -= drain_inc; + } + + dac_write(MCP4725_ID_DRAIN_VOLT, drain_count, 1); + _delay_us(DRAIN_LOOP_DELAY_US); + + fw_pwr_adc = adc_read_n(MCP3202_ID_FW_POWER, DRAIN_POWER_SAMPLES); + util_count_to_pwr(fw_pwr_adc, &fw_pwr); + + if (fw_pwr <= (target_fw_pwr + test_boundary) && + fw_pwr >= (target_fw_pwr - test_boundary)) + { + test_count += 1; + } + } + + system_status.d_time = (uint16_t)(timer_millis() - time_start); + + if (timeout) + { + system_status.status = SYSTEM_STATUS_SETPOINT_TIMEOUT; + } + else if(test_count >= DRAIN_TEST_COUNT) + { + system_status.status = SYSTEM_STATUS_SETPOINT_REACHED; + } + else + { + system_status.status = SYSTEM_STATUS_SETPOINT_FAIL; + } + + return test_count; +} diff --git a/firmware/src/module/gate.c b/firmware/src/module/gate.c new file mode 100644 index 0000000..b143eab --- /dev/null +++ b/firmware/src/module/gate.c @@ -0,0 +1,78 @@ +/** + * + * Author: Dylan Muller + * Copyright (c) 2025 + * All rights reserved. + * + * - Commercial/IP use prohibited. + * - Attribution required. + * See License.txt + * + */ + +#include "setup.h" + +#include "driver/mcp3202.h" +#include "driver/mcp4725.h" + +#include "module/gate.h" + +#include "util.h" +#include "status.h" + +#include <avr/interrupt.h> +#include <util/delay.h> + +extern t_gate_status gate_status; +extern t_system_status system_status; + +uint16_t gate_probe( + uint16_t target_amp, + uint16_t* probe_result +) +{ + uint16_t probe_volt = 0x0; + uint16_t drain_amp = 0x0; + uint16_t gate_count = 0x0; + uint16_t drain_count = 0x0; + + system_status.status = SYSTEM_STATUS_BUSY; + + util_volt_to_count(GATE_PROBE_DRAIN, &drain_count); + util_volt_to_count(GATE_PROBE_START, &gate_count ); + + dac_write(MCP4725_ID_GATE_VOLT, gate_count, 0); + _delay_ms(TRANSACT_DELAY_MS); + dac_write(MCP4725_ID_DRAIN_VOLT, drain_count, 0); + _delay_ms(TRANSACT_DELAY_MS); + + while((drain_amp < target_amp) && probe_volt < GATE_PROBE_LIMIT) + { + gate_count += GATE_PROBE_INC; + dac_write(MCP4725_ID_GATE_VOLT, gate_count, 0); + _delay_us(GATE_LOOP_DELAY_US); + + drain_count = adc_read(MCP3202_ID_DRAIN_AMP, 0); + util_count_to_volt(drain_count, &drain_amp); + util_count_to_volt(gate_count, &probe_volt); + } + + _delay_ms(TRANSACT_DELAY_MS); + drain_count = adc_read(MCP3202_ID_DRAIN_AMP, 0); + util_count_to_volt(drain_count, &drain_amp); + + if(GATE_PA_POWER_DOWN) + { + _delay_ms(TRANSACT_DELAY_MS); + dac_write(MCP4725_ID_DRAIN_VOLT, 0, 0); + } + + gate_status.q_amp = drain_amp; + gate_status.q_volt = probe_volt; + + *probe_result = probe_volt; + + system_status.status = SYSTEM_STATUS_IDLE; + + return drain_amp; +} diff --git a/firmware/src/peripheral/ring.c b/firmware/src/peripheral/ring.c new file mode 100644 index 0000000..eacb654 --- /dev/null +++ b/firmware/src/peripheral/ring.c @@ -0,0 +1,115 @@ +/** + * + * Author: Dylan Muller + * Copyright (c) 2025 + * All rights reserved. + * + * - Commercial/IP use prohibited. + * - Attribution required. + * See License.txt + * + */ + +#include "peripheral/ring.h" + +#define RING_INC_ROLL_OVER(n, s, e) (((n)+1>=(e)) ? (s) : (n)+1) + +ring_t ring_init( + uint8_t *buf, + size_t len +) +{ + return RING_INIT(buf, len); +} + +bool ring_is_empty( + ring_t ring +) +{ + return ring.read == ring.write; +} + +bool ring_is_full( + ring_t ring +) +{ + return RING_INC_ROLL_OVER(ring.write, ring.buf, ring.end) == ring.read; +} + +size_t ring_push_available( + ring_t ring +) +{ + if(ring.write < ring.read) + return ring.read - ring.write - 1; + else + return (ring.end - ring.buf) - (ring.write - ring.read) - 1; +} + +size_t ring_pop_available( + ring_t ring +) +{ + if(ring.read <= ring.write) + return ring.write - ring.read; + else + return (ring.end - ring.buf) - (ring.read - ring.write); +} + +bool ring_push( + ring_t *ring, + uint8_t data +) +{ + if(ring_is_full(*ring)) + return 1; + + *ring->write = data; + ring->write = RING_INC_ROLL_OVER(ring->write, ring->buf, ring->end); + + return 0; +} + +bool ring_push_over( + ring_t *ring, + uint8_t data +) +{ + *ring->write = data; + ring->write = RING_INC_ROLL_OVER(ring->write, ring->buf, ring->end); + + if(ring->read == ring->write) + { + ring->read = RING_INC_ROLL_OVER(ring->read, ring->buf, ring->end); + return 1; + } + + return 0; +} + +bool ring_pop( + ring_t *ring, + uint8_t *data +) +{ + if(ring_is_empty(*ring)) + return 1; + + *data = *ring->read; + ring->read = RING_INC_ROLL_OVER(ring->read, ring->buf, ring->end); + + return 0; +} + +bool ring_peek( + ring_t *ring, + uint8_t *data +) +{ + if(ring_is_empty(*ring)) + return 1; + + *data = *ring->read; + + return 0; +} diff --git a/firmware/src/peripheral/spi.c b/firmware/src/peripheral/spi.c new file mode 100644 index 0000000..884985d --- /dev/null +++ b/firmware/src/peripheral/spi.c @@ -0,0 +1,106 @@ +/** + * + * Author: Dylan Muller + * Copyright (c) 2025 + * All rights reserved. + * + * - Commercial/IP use prohibited. + * - Attribution required. + * See License.txt + * + */ + +#include "setup.h" + +#include "peripheral/spi.h" + +#include <stdio.h> +#include <avr/io.h> +#include <avr/interrupt.h> +#include <util/delay.h> + +#if SPI_MODE == 0 + #define CPOL_VALUE 0 + #define CPHA_VALUE 0 +#elif SPI_MODE == 1 + #define CPOL_VALUE 0 + #define CPHA_VALUE 1 +#elif SPI_MODE == 2 + #define CPOL_VALUE 1 + #define CPHA_VALUE 0 +#elif SPI_MODE == 3 + #define CPOL_VALUE 1 + #define CPHA_VALUE 1 +#else + #error "No valid SPI_MODE defined!" +#endif + +#if SPI_DORD == 0 + #define DORD_VALUE 0 +#elif SPI_DORD == 1 + #define DORD_VALUE 1 +#else + #error "No valid SPI_DORD defined!" +#endif + +static volatile uint8_t *spi_out; +static volatile uint8_t *spi_in; +static volatile size_t spi_len; +static volatile uint8_t *spi_port; +static volatile uint8_t spi_pin; + +void spi_init(void) +{ + DDRB |= (1 << DDB2) | (1 << DDB3) | (1 << DDB5); + + SPSR |= (SPI2X_VALUE << SPI2X); + SPCR |= (1 << SPIE) | (1 << SPE) | (DORD_VALUE << DORD) | (1 << MSTR) + | (CPOL_VALUE << CPOL) | (CPHA_VALUE << CPHA) + | (SPR1_VALUE << SPR1) | (SPR0_VALUE << SPR0); +} + +bool spi_busy(void) +{ + return spi_len; +} + +void spi_flush(void) +{ + while(spi_len); +} + +void spi_start( + uint8_t *out, + uint8_t *in, + size_t len, + uint8_t *port, + uint8_t pin +) +{ + spi_flush(); + + spi_out = out; + spi_in = in; + spi_len = len; + spi_port = port; + spi_pin = pin; + + if(spi_port) + *spi_port &= ~(1 << spi_pin); + + SPDR = *spi_out++; +} + +ISR(SPI_STC_vect) +{ + if(spi_in) + { + *spi_in++ = SPDR; + } + + if(--spi_len) + SPDR = *spi_out++; + else + if(spi_port) + *spi_port |= (1 << spi_pin); +} diff --git a/firmware/src/peripheral/timer.c b/firmware/src/peripheral/timer.c new file mode 100644 index 0000000..1caff1c --- /dev/null +++ b/firmware/src/peripheral/timer.c @@ -0,0 +1,57 @@ +/** + * + * Author: Dylan Muller + * Copyright (c) 2025 + * All rights reserved. + * + * - Commercial/IP use prohibited. + * - Attribution required. + * See License.txt + * + */ + +#include "setup.h" + +#include "peripheral/timer.h" + +#include <stdint.h> +#include <avr/io.h> +#include <avr/interrupt.h> +#include <util/atomic.h> + +uint32_t millis_count = 0; + +void timer_init(void) +{ + uint32_t ctc_overflow; + + ctc_overflow = ((F_CPU / 1000) / 8); + TCCR1B |= (1 << WGM12) | (1 << CS11); + + OCR1AH = (ctc_overflow >> 8); + OCR1AL = ctc_overflow; + + TIMSK1 |= (1 << OCIE1A); +} + +void timer_reset(void) +{ + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + millis_count = 0; + } +} + +uint32_t timer_millis(void) +{ + uint32_t millis; + + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + millis = millis_count; + } + return millis; +} + +ISR(TIMER1_COMPA_vect) +{ + millis_count++; +} diff --git a/firmware/src/peripheral/twi.c b/firmware/src/peripheral/twi.c new file mode 100644 index 0000000..671490b --- /dev/null +++ b/firmware/src/peripheral/twi.c @@ -0,0 +1,114 @@ +/** + * + * Author: Dylan Muller + * Copyright (c) 2025 + * All rights reserved. + * + * - Commercial/IP use prohibited. + * - Attribution required. + * See License.txt + * + */ + +#include "setup.h" + +#include "peripheral/twi.h" + +#include <avr/io.h> +#include <avr/interrupt.h> +#include <util/delay.h> +#include <util/twi.h> + +#define TWBR_VALUE ((F_CPU/TWI_FREQUENCY - 16) / (2 * TWI_PRESCALER)) + +static uint8_t twi_address; +static uint8_t* twi_data; +static size_t twi_index; +static size_t twi_len; + +void twi_init(void) +{ + TWBR = TWBR_VALUE; + TWSR = (TWPS1_VALUE << TWPS1) | (TWPS0_VALUE << TWPS0); + + TWCR = (1 << TWINT) | (1 << TWEN); +} + +bool twi_busy(void) +{ + return TWCR & (1<<TWIE); +} + +void twi_flush(void) +{ + while(TWCR & (1<<TWIE)); +} + +void twi_start( + uint8_t address, + uint8_t *data, + size_t len +) +{ + twi_flush(); + + twi_address = address; + twi_data = data; + twi_len = len; + + TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN) | (1 << TWIE); +} + +ISR(TWI_vect) +{ + switch(TW_STATUS) + { + case TW_START: + case TW_REP_START: + twi_index = 0; + TWDR = twi_address; + TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWIE); + break; + + case TW_MT_SLA_ACK: + case TW_MT_DATA_ACK: + if(twi_index < twi_len) + { + TWDR = twi_data[twi_index++]; + TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWIE); + } + else + { + TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO); + } + break; + + case TW_MR_DATA_ACK: + twi_data[twi_index++] = TWDR; + case TW_MR_SLA_ACK: + if(twi_index < twi_len-1) + { + TWCR = (1 << TWINT) | (1 << TWEA) | (1 << TWEN) | (1 << TWIE); + } + else + { + TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWIE); + } + break; + + case TW_MR_DATA_NACK: + twi_data[twi_index++] = TWDR; + TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO); + break; + + case TW_MT_ARB_LOST: + TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN) | (1 << TWIE); + break; + + case TW_MT_SLA_NACK: + case TW_MT_DATA_NACK: + case TW_MR_SLA_NACK: + default: + TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO); + } +} diff --git a/firmware/src/peripheral/uart.c b/firmware/src/peripheral/uart.c new file mode 100644 index 0000000..defcf54 --- /dev/null +++ b/firmware/src/peripheral/uart.c @@ -0,0 +1,239 @@ +/** + * + * Author: Dylan Muller + * Copyright (c) 2025 + * All rights reserved. + * + * - Commercial/IP use prohibited. + * - Attribution required. + * See License.txt + * + */ + +#include "setup.h" + +#include "peripheral/ring.h" +#include "peripheral/uart.h" + +#include "status.h" +#include "cmd.h" + +#include <avr/io.h> +#include <avr/interrupt.h> +#include <util/atomic.h> +#include <util/setbaud.h> + +extern t_system_status system_status; + +static int uart_putc( + char c, + FILE *stream +) +{ + (void)stream; + + if(uart_tx(c)) + return _FDEV_EOF; + + return c; +} + +static int uart_getc( + FILE *stream +) +{ + uint8_t c; + (void)stream; + + if(uart_rx(&c)) + return _FDEV_EOF; + + return c; +} + +FILE uart_out = FDEV_SETUP_STREAM(uart_putc, NULL, _FDEV_SETUP_WRITE); +FILE uart_in = FDEV_SETUP_STREAM(NULL, uart_getc, _FDEV_SETUP_READ); + +static volatile ring_t uart_tx_ring; +static volatile ring_t uart_rx_ring; + +static volatile uint8_t uart_tx_buf[UART_BUF_SIZE]; +static volatile uint8_t uart_rx_buf[UART_BUF_SIZE]; + +void uart_init(void) +{ + uart_tx_ring = ring_init((uint8_t*)uart_tx_buf, UART_BUF_SIZE - 1); + uart_rx_ring = ring_init((uint8_t*)uart_rx_buf, UART_BUF_SIZE - 1); + + UBRR0H = UBRRH_VALUE; + UBRR0L = UBRRL_VALUE; + UCSR0B |= (1 << RXCIE0) | (1 << RXEN0) | (1 << TXEN0); + UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); + + stdout = &uart_out; + stdin = &uart_in; +} + +bool uart_tx( + uint8_t data +) +{ + bool ret; + + while(ring_is_full(uart_tx_ring)); + + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) + { + ret = ring_push((ring_t*)&uart_tx_ring, data); + } + + if(!ret) + UCSR0B |= (1 << UDRIE0); + + return ret; +} + +bool uart_rx( + uint8_t* data +) +{ + bool ret; + + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) + { + ret = ring_pop((ring_t*)&uart_rx_ring, data); + } + + return ret; +} + +size_t uart_tx_burst( + uint8_t *data, + size_t size +) +{ + size_t i = 0; + + while(i<size && !uart_tx(*data++)) + i++; + + return i; +} + +size_t uart_rx_burst( + uint8_t* data, + size_t len +) +{ + size_t i = 0; + + while(i<len && !uart_rx(data++)) + i++; + + return i; +} + +size_t uart_tx_available(void) +{ + size_t ret; + + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) + { + ret = ring_push_available(uart_tx_ring); + } + + return ret; +} + +size_t uart_rx_available(void) +{ + size_t ret; + + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) + { + ret = ring_pop_available(uart_rx_ring); + } + + return ret; +} + +bool uart_rx_peek( + uint8_t *data +) +{ + bool ret; + + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) + { + ret = ring_peek((ring_t*)&uart_rx_ring, data); + } + + return ret; +} + +void uart_tx_flush(void) +{ + bool empty; + do + { + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) + { + empty = ring_is_empty(uart_tx_ring); + } + } while(!empty); +} + +char *uart_ngets(char *s, size_t n) +{ + uint8_t c; + static size_t i = 0; + + + while(!uart_rx(&c)) + { + s[i++] = c; + + if(c == '\n' || i >= n-1) + { + s[i] = '\0'; + i = 0; + + return s; + } + } + + return NULL; +} + +ISR(USART_UDRE_vect) +{ + uint8_t c; + + if(!ring_pop((ring_t*)&uart_tx_ring, &c)) + UDR0 = c; + + else + UCSR0B &= ~(1 << UDRIE0); +} + +ISR(USART_RX_vect) +{ + if (system_status.status == SYSTEM_STATUS_BUSY) + { + // system busy.. lock now! + CMD_ERROR("SYSTEM BUSY"); + system_status.status = SYSTEM_STATUS_LOCKED; + } + + if (system_status.status != SYSTEM_STATUS_LOCKED) + { + #ifdef UART_OVERWRITE + ring_push_over((ring_t*)&uart_rx_ring, UDR0); + #else + ring_push((ring_t*)&uart_rx_ring, UDR0); + #endif + } + else{ + (uint8_t)UDR0; + } +} diff --git a/firmware/src/status.c b/firmware/src/status.c new file mode 100644 index 0000000..d3bb9eb --- /dev/null +++ b/firmware/src/status.c @@ -0,0 +1,46 @@ +/** + * + * Author: Dylan Muller + * Copyright (c) 2025 + * All rights reserved. + * + * - Commercial/IP use prohibited. + * - Attribution required. + * See License.txt + * + */ + +#include "status.h" +#include <avr/pgmspace.h> + +t_gate_status gate_status; +t_drain_status drain_status; +t_system_status system_status; + +const char* const gate_lut[] PROGMEM = +{ + "INACTIVE", + "PROBE_ERR", + "PROBE_OK", + "PROBE_TIME" +}; + +const char* const drain_lut[] PROGMEM = +{ + "INACTIVE", + "POWER_SET", + "OVERRIDE", + "ADC_ERROR", + "AMP_ALARM" +}; + +const char* const system_lut[] PROGMEM = +{ + "IDLE", + "BUSY", + "LOCKED", + "SET_FAIL", + "SET_REACHED", + "SET_TIME", + "VSWR_ALARM" +}; diff --git a/firmware/src/util.c b/firmware/src/util.c new file mode 100644 index 0000000..af6ffed --- /dev/null +++ b/firmware/src/util.c @@ -0,0 +1,152 @@ +/** + * + * Author: Dylan Muller + * Copyright (c) 2025 + * All rights reserved. + * + * - Commercial/IP use prohibited. + * - Attribution required. + * See License.txt + * + */ + +#include "util.h" + +#include <stdio.h> +#include <avr/pgmspace.h> + +extern int __heap_start, *__brkval; + +const uint16_t fw_pwr_gradient[] = { 249, 356, 374, 388, 400, 402, 435, 460, 500, 553 }; +const int16_t fw_pwr_intercept[] = { -5418, -3810, -3602, -3431, -3289, -3261, -2877, -2601, -2189, -1725 }; +const uint16_t fw_pwr_threshold[] = { 1288, 1548, 1704, 1812, 1897, 1967, 2164, 2301, 2411, 2505 }; + +uint8_t util_volt_to_count( + uint16_t volt, + uint16_t* count +) +{ + uint8_t bits = 0x0; + uint32_t x = 0x0; + uint32_t y = 0x0; + uint16_t i = 0x0; + + bits = UTIL_CONV_BITS; + + if (volt > UTIL_SYSTEM_VOLT) + { + return 1; + } + + for (i = 0; i < bits; i++) + { + x |= UTIL_CONV_BIT(i); + } + + y = ((uint32_t)volt) * x; + y /= UTIL_SYSTEM_VOLT; + + *count = (uint16_t)y; + return 0; +} + +uint8_t util_count_to_volt( + uint16_t count, + uint16_t* volt +) +{ + uint8_t bits = 0x0; + uint32_t x = 0x0; + uint32_t y = 0x0; + uint16_t i = 0x0; + + bits = UTIL_CONV_BITS; + + for (i = 0; i < bits; i++) + { + x |= UTIL_CONV_BIT(i); + } + + if (count > x) + { + return 1; + } + + y = ((uint32_t)count) * UTIL_SYSTEM_VOLT; + y /= x; + + *volt = (uint16_t)y; + return 0; +} + +int8_t util_count_to_pwr( + uint16_t count, + int16_t* cpwr +) +{ + uint32_t x = 0x0; + int16_t y = 0x0; + uint16_t slope = 0x0; + int16_t intercept = 0x0; + uint16_t i = 0; + + uint8_t pwr_cal_size = sizeof(fw_pwr_gradient) / sizeof(uint16_t); + uint8_t pwr_last_element = pwr_cal_size - 1; + + if(count >= fw_pwr_threshold[pwr_last_element]) + { + slope = fw_pwr_gradient[pwr_last_element]; + intercept = fw_pwr_intercept[pwr_last_element]; + } + else if (count <= fw_pwr_threshold[0]) + { + slope = fw_pwr_gradient[0]; + intercept = fw_pwr_intercept[0]; + } + else + { + for(i = 0; i < pwr_cal_size; i++) + { + if(count < fw_pwr_threshold[i]) + { + slope = fw_pwr_gradient[i]; + intercept = fw_pwr_intercept[i]; + break; + } + } + } + + x = (((uint32_t)count * 1000) / slope); + y = (int16_t)x; + + y = y + intercept; + *cpwr = y; + + return 0; +} + +void util_print_pwr( + int16_t cpwr +) +{ + int16_t integer_part = cpwr / 100; + + int16_t fractional_part = cpwr % 100; + + if (fractional_part < 0) { + fractional_part = -fractional_part; + } + + printf("%d.%02d dBm\n", integer_part, fractional_part); +} + +uint16_t util_get_free_mem(void) +{ + uint16_t free_mem; + if ((int)__brkval == 0) { + free_mem = (int)&free_mem - (int)&__heap_start; + } else { + free_mem = (int)&free_mem - (int)__brkval; + } + return free_mem; +} |
