Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ The real-time operating system for Brown Space Engineering's second satellite, P
- Log output can be viewed by running `python3 scripts/rtt_logs.py` in a separate terminal window. This will also record logs to the `/logs` folder.
- If the script fails to run, you may need to install 'pylink-square' (`pip install pylink-square`)
- Alternatively, you can try running `python3 scripts/rtt_splitscreen.py` for both the PVDXos Shell and log output in the same terminal window, but this might not work!
- To sample the internal temperature sensor from the shell, run `temperature` (add the optional `raw` argument to display PTAT/CTAT diagnostic values). The command initializes the sensor if needed and prints the reading in Celsius.

## Toolchain Installation

Expand Down
4 changes: 4 additions & 0 deletions src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ export OBJS := \
../src/tasks/photodiode/photodiode_main.o \
../src/tasks/photodiode/photodiode_task.o \
../src/tasks/photodiode/photodiode_driver.o \
../src/tasks/temperature/temperature_driver.o \
../src/tasks/temperature/temperature_task.o \
../src/tasks/temperature/temperature_main.o \

### ALL DIRECTORIES WITH SOURCE FILES MUST BE LISTED HERE ###
### THESE ARE WRITTEN RELATIVE TO THE ./ASF/gcc/Makefile FILE ###
Expand All @@ -85,6 +88,7 @@ export EXTRA_VPATH := \
../../src/tasks/magnetometer \
../../src/tasks/shell \
../../src/tasks/photodiode \
../../src/tasks/temperature \
../../src/mutexes


Expand Down
4 changes: 3 additions & 1 deletion src/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,12 @@ typedef enum {

// Magnetometer operations
OPERATION_READ, // p_data: magnetometer_read_args_t *readings

// Photodiode operations
OPERATION_PHOTODIODE_READ,

// Temperature operations
OPERATION_TEMPERATURE_READ,

// TESTING
TEST_OP, // p_data: char message[]
} operation_t;
Expand Down
45 changes: 45 additions & 0 deletions src/tasks/shell/shell_commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#include "logging.h"
#include "shell_helpers.h"
#include "temperature_driver.h"
#include "watchdog_task.h"

shell_command_t shell_commands[] = {
Expand All @@ -22,6 +23,7 @@ shell_command_t shell_commands[] = {
{"loglevel", shell_loglevel, help_loglevel},
{"reboot", shell_reboot, help_reboot},
{"display", shell_display, help_display},
{"temperature", shell_temperature, help_temperature},
{NULL, NULL, NULL} // Null-terminated array
};

Expand Down Expand Up @@ -259,4 +261,47 @@ void shell_display(char **args, int arg_count) {
void help_display() {
terminal_printf("Usage: display\n");
terminal_printf("\tgjnerergjkn\n");
}

/* TEMPERATURE COMMAND */

void shell_temperature(char **args, int arg_count) {
bool show_raw = false;
if (arg_count > 2) {
terminal_printf("Invalid usage. Try 'help temperature'\n");
return;
}
if (arg_count == 2) {
if (strcmp(args[1], "raw") == 0) {
show_raw = true;
} else {
terminal_printf("Invalid option '%s'. Try 'help temperature'\n", args[1]);
return;
}
}

status_t status = temp_sensor_init();
if (status != SUCCESS) {
terminal_printf("temperature: init failed (%d)\n", status);
return;
}

temp_sensor_sample_t sample = {0};
status = temp_sensor_sample(&sample);
if (status != SUCCESS) {
terminal_printf("temperature: sample failed (%d)\n", status);
return;
}

terminal_printf("Temperature: %.2f C\n", sample.temperature_c);
if (show_raw) {
terminal_printf(" PTAT: raw=%u -> %.2f C\n", sample.ptat_raw, sample.temperature_ptat_c);
terminal_printf(" CTAT: raw=%u -> %.2f C\n", sample.ctat_raw, sample.temperature_ctat_c);
}
}

void help_temperature() {
terminal_printf("Usage: temperature [raw]\n");
terminal_printf("\tSamples the onboard temperature sensor and prints the result in Celsius.\n");
terminal_printf("\tPass 'raw' to also display PTAT/CTAT channels and their converted estimates.\n");
}
3 changes: 3 additions & 0 deletions src/tasks/shell/shell_commands.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,7 @@ void help_reboot();
void shell_display(char **args, int arg_count);
void help_display();

void shell_temperature(char **args, int arg_count);
void help_temperature();

#endif // SHELL_COMMANDS_H
20 changes: 20 additions & 0 deletions src/tasks/task_list.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,24 @@ pvdx_task_t photodiode_task = {
.task_type = SENSOR
};

pvdx_task_t temperature_task = {
.name = "Temperature",
.enabled = false,
.handle = NULL,
.command_queue = NULL,
.init = init_temperature,
.function = main_temperature,
.stack_size = TEMPERATURE_TASK_STACK_SIZE,
.stack_buffer = temperature_mem.temperature_task_stack,
.pvParameters = NULL,
.priority = 2,
.task_tcb = &temperature_mem.temperature_task_tcb,
.watchdog_timeout_ms = 5000,
.last_checkin_time_ticks = 0xDEADBEEF,
.has_registered = false,
.task_type = SENSOR
};

pvdx_task_t shell_task = {
.name = "Shell",
.enabled = false,
Expand Down Expand Up @@ -161,6 +179,7 @@ pvdx_task_t *const p_command_dispatcher_task = &command_dispatcher_task;
pvdx_task_t *const p_task_manager_task = &task_manager_task;
pvdx_task_t *const p_magnetometer_task = &magnetometer_task;
pvdx_task_t *const p_photodiode_task = &photodiode_task;
pvdx_task_t *const p_temperature_task = &temperature_task;
pvdx_task_t *const p_shell_task = &shell_task;
pvdx_task_t *const p_display_task = &display_task;
pvdx_task_t *const p_heartbeat_task = &heartbeat_task;
Expand All @@ -178,6 +197,7 @@ pvdx_task_t *task_list[] = {
p_task_manager_task,
p_magnetometer_task,
p_photodiode_task,
p_temperature_task,
p_shell_task,
p_display_task,
p_heartbeat_task,
Expand Down
2 changes: 2 additions & 0 deletions src/tasks/task_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "heartbeat_task.h"
#include "magnetometer_task.h"
#include "photodiode_task.h"
#include "temperature_task.h"
#include "shell_task.h"
#include "task_manager_task.h"
#include "watchdog_task.h"
Expand All @@ -18,6 +19,7 @@ extern pvdx_task_t *const p_command_dispatcher_task;
extern pvdx_task_t *const p_task_manager_task;
extern pvdx_task_t *const p_magnetometer_task;
extern pvdx_task_t *const p_photodiode_task;
extern pvdx_task_t *const p_temperature_task;
extern pvdx_task_t *const p_shell_task;
extern pvdx_task_t *const p_display_task;
extern pvdx_task_t *const p_heartbeat_task;
Expand Down
211 changes: 211 additions & 0 deletions src/tasks/temperature/temperature_driver.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
#include "temperature_driver.h"

#include "logging.h"

#include <hri_supc_d51.h>
#include <math.h>
#include <string.h>

#define TEMP_SENSOR_ADC_INSTANCE ADC1

#define TEMP_SENSOR_DEFAULT_PRESCALER ADC_CTRLA_PRESCALER_DIV32
#define TEMP_SENSOR_DEFAULT_SAMPLENUM ADC_AVGCTRL_SAMPLENUM_32
#define TEMP_SENSOR_DEFAULT_ADJRES 5
#define TEMP_SENSOR_DEFAULT_SAMPLEN 5

typedef struct {
bool loaded;
float room_temp_c;
float hot_temp_c;
uint16_t room_ptat;
uint16_t hot_ptat;
uint16_t room_ctat;
uint16_t hot_ctat;
float ptat_slope;
float ptat_intercept;
float ctat_slope;
float ctat_intercept;
} temp_calibration_t;

static temp_calibration_t g_calibration = {0};
static bool g_temp_sensor_initialized = false;

static inline void wait_sync(uint32_t mask) {
while (TEMP_SENSOR_ADC_INSTANCE->SYNCBUSY.reg & mask) {
}
}

static inline float decode_temperature(uint8_t integer_part, uint8_t fractional_part) {
return (float)integer_part + ((float)fractional_part / 16.0f);
}

static status_t load_calibration(temp_calibration_t *cal) {
if (!cal) {
return ERROR_SANITY_CHECK_FAILED;
}

memset(cal, 0, sizeof(*cal));

const uint32_t temp_log0 = *((uint32_t *)NVMCTRL_TEMP_LOG);
const uint32_t temp_log1 = *((uint32_t *)(NVMCTRL_TEMP_LOG + 4U));
const uint32_t temp_log2 = *((uint32_t *)(NVMCTRL_TEMP_LOG + 8U));

const uint8_t room_temp_int = (temp_log0 & FUSES_ROOM_TEMP_VAL_INT_Msk) >> FUSES_ROOM_TEMP_VAL_INT_Pos;
const uint8_t room_temp_dec = (temp_log0 & FUSES_ROOM_TEMP_VAL_DEC_Msk) >> FUSES_ROOM_TEMP_VAL_DEC_Pos;
const uint8_t hot_temp_int = (temp_log0 & FUSES_HOT_TEMP_VAL_INT_Msk) >> FUSES_HOT_TEMP_VAL_INT_Pos;
const uint8_t hot_temp_dec = (temp_log0 & FUSES_HOT_TEMP_VAL_DEC_Msk) >> FUSES_HOT_TEMP_VAL_DEC_Pos;

const uint16_t room_ptat_raw = (temp_log1 & FUSES_ROOM_ADC_VAL_PTAT_Msk) >> FUSES_ROOM_ADC_VAL_PTAT_Pos;
const uint16_t hot_ptat_raw = (temp_log1 & FUSES_HOT_ADC_VAL_PTAT_Msk) >> FUSES_HOT_ADC_VAL_PTAT_Pos;

const uint16_t room_ctat_raw = (temp_log2 & FUSES_ROOM_ADC_VAL_CTAT_Msk) >> FUSES_ROOM_ADC_VAL_CTAT_Pos;
const uint16_t hot_ctat_raw = (temp_log2 & FUSES_HOT_ADC_VAL_CTAT_Msk) >> FUSES_HOT_ADC_VAL_CTAT_Pos;

cal->room_temp_c = decode_temperature(room_temp_int, room_temp_dec);
cal->hot_temp_c = decode_temperature(hot_temp_int, hot_temp_dec);

const float delta_temp = cal->hot_temp_c - cal->room_temp_c;
if (fabsf(delta_temp) < 0.01f) {
warning("temperature: calibration delta temperature too small (%.2f)", delta_temp);
return ERROR_SANITY_CHECK_FAILED;
}

cal->room_ptat = room_ptat_raw;
cal->hot_ptat = hot_ptat_raw;
cal->room_ctat = room_ctat_raw;
cal->hot_ctat = hot_ctat_raw;

cal->ptat_slope = ((float)hot_ptat_raw - (float)room_ptat_raw) / delta_temp;
cal->ctat_slope = ((float)hot_ctat_raw - (float)room_ctat_raw) / delta_temp;

if (fabsf(cal->ptat_slope) < 1e-6f || fabsf(cal->ctat_slope) < 1e-6f) {
warning("temperature: calibration slope too small (ptat %.6f, ctat %.6f)", cal->ptat_slope, cal->ctat_slope);
return ERROR_SANITY_CHECK_FAILED;
}

cal->ptat_intercept = (float)room_ptat_raw - (cal->ptat_slope * cal->room_temp_c);
cal->ctat_intercept = (float)room_ctat_raw - (cal->ctat_slope * cal->room_temp_c);
cal->loaded = true;

return SUCCESS;
}

static inline uint16_t read_adc_result(void) {
while (!(TEMP_SENSOR_ADC_INSTANCE->INTFLAG.reg & ADC_INTFLAG_RESRDY)) {
}
const uint16_t result = TEMP_SENSOR_ADC_INSTANCE->RESULT.reg;
TEMP_SENSOR_ADC_INSTANCE->INTFLAG.reg = ADC_INTFLAG_RESRDY | ADC_INTFLAG_OVERRUN;
return result;
}

static uint16_t sample_internal_channel(uint8_t muxpos) {
wait_sync(ADC_SYNCBUSY_INPUTCTRL);
TEMP_SENSOR_ADC_INSTANCE->INPUTCTRL.reg =
ADC_INPUTCTRL_MUXPOS(muxpos) | ADC_INPUTCTRL_MUXNEG_GND;
wait_sync(ADC_SYNCBUSY_INPUTCTRL);

TEMP_SENSOR_ADC_INSTANCE->SWTRIG.reg |= ADC_SWTRIG_START;
wait_sync(ADC_SYNCBUSY_SWTRIG);
return read_adc_result();
}

static status_t ensure_adc_ready(void) {
if (!(TEMP_SENSOR_ADC_INSTANCE->CTRLA.reg & ADC_CTRLA_ENABLE)) {
TEMP_SENSOR_ADC_INSTANCE->CTRLA.reg |= ADC_CTRLA_ENABLE;
wait_sync(ADC_SYNCBUSY_ENABLE);
}
return SUCCESS;
}

status_t temp_sensor_init(void) {
if (g_temp_sensor_initialized) {
return SUCCESS;
}

debug("temperature: initializing internal temperature sensor driver\n");

status_t status = load_calibration(&g_calibration);
if (status != SUCCESS) {
return status;
}

hri_supc_set_VREF_TSEN_bit(SUPC);

if (TEMP_SENSOR_ADC_INSTANCE->CTRLA.reg & ADC_CTRLA_ENABLE) {
TEMP_SENSOR_ADC_INSTANCE->CTRLA.reg &= ~ADC_CTRLA_ENABLE;
wait_sync(ADC_SYNCBUSY_ENABLE);
}

TEMP_SENSOR_ADC_INSTANCE->CTRLA.reg &= ~ADC_CTRLA_PRESCALER_Msk;
TEMP_SENSOR_ADC_INSTANCE->CTRLA.reg |= TEMP_SENSOR_DEFAULT_PRESCALER;

wait_sync(ADC_SYNCBUSY_REFCTRL);
TEMP_SENSOR_ADC_INSTANCE->REFCTRL.reg = ADC_REFCTRL_REFSEL_INTREF;

wait_sync(ADC_SYNCBUSY_AVGCTRL);
TEMP_SENSOR_ADC_INSTANCE->AVGCTRL.reg =
TEMP_SENSOR_DEFAULT_SAMPLENUM | ADC_AVGCTRL_ADJRES(TEMP_SENSOR_DEFAULT_ADJRES);

wait_sync(ADC_SYNCBUSY_SAMPCTRL);
TEMP_SENSOR_ADC_INSTANCE->SAMPCTRL.reg = ADC_SAMPCTRL_SAMPLEN(TEMP_SENSOR_DEFAULT_SAMPLEN);

wait_sync(ADC_SYNCBUSY_CTRLB);
TEMP_SENSOR_ADC_INSTANCE->CTRLB.reg = ADC_CTRLB_RESSEL_12BIT;

TEMP_SENSOR_ADC_INSTANCE->INTFLAG.reg = ADC_INTFLAG_MASK;

wait_sync(ADC_SYNCBUSY_ENABLE);
TEMP_SENSOR_ADC_INSTANCE->CTRLA.reg |= ADC_CTRLA_ENABLE;
wait_sync(ADC_SYNCBUSY_ENABLE);

g_temp_sensor_initialized = true;
info("temperature: driver initialized (room %.2fC, hot %.2fC)\n",
g_calibration.room_temp_c, g_calibration.hot_temp_c);
return SUCCESS;
}

status_t temp_sensor_sample(temp_sensor_sample_t *sample) {
if (!sample) {
return ERROR_SANITY_CHECK_FAILED;
}
if (!g_temp_sensor_initialized || !g_calibration.loaded) {
return ERROR_NOT_READY;
}

taskENTER_CRITICAL();
ensure_adc_ready();
const uint16_t ptat = sample_internal_channel(ADC1_PTAT);
const uint16_t ctat = sample_internal_channel(ADC1_CTAT);
taskEXIT_CRITICAL();

const float ptat_norm = (float)ptat;
const float ctat_norm = (float)ctat;

const float temp_ptat =
(ptat_norm - g_calibration.ptat_intercept) / g_calibration.ptat_slope;
const float temp_ctat =
(ctat_norm - g_calibration.ctat_intercept) / g_calibration.ctat_slope;

sample->ptat_raw = ptat;
sample->ctat_raw = ctat;
sample->temperature_ptat_c = temp_ptat;
sample->temperature_ctat_c = temp_ctat;
sample->temperature_c = (temp_ptat + temp_ctat) * 0.5f;

return SUCCESS;
}

status_t temp_sensor_get_celsius(float *temperature_c) {
if (!temperature_c) {
return ERROR_SANITY_CHECK_FAILED;
}
temp_sensor_sample_t sample = {0};
status_t status = temp_sensor_sample(&sample);
if (status != SUCCESS) {
return status;
}

*temperature_c = sample.temperature_c;
return SUCCESS;
}

Loading