Skip to content
Open
Show file tree
Hide file tree
Changes from 122 commits
Commits
Show all changes
153 commits
Select commit Hold shift + click to select a range
504c535
Generate peripheral drivers with MCC
jhub04 Oct 5, 2025
fefd480
feat: enable LED as output
jhub04 Oct 8, 2025
2ec4fad
feat/change pwm frequency to 50Hz
jhub04 Oct 9, 2025
eed639e
feat/configure CAN1 TX and RX on PB12 and PB13 respectively
jhub04 Oct 9, 2025
a4ecb80
feat: change CAN FD bit rates
jhub04 Oct 9, 2025
8a237c5
feat: configure SERCOM1 as I2C master
jhub04 Oct 9, 2025
319c78a
feat: reconfigure tcc periods and prescalers
jhub04 Oct 9, 2025
251d340
feat: create table of thrusters' tcc channels
jhub04 Oct 9, 2025
b4cf015
feat: add method to set PWM dutycycle
jhub04 Oct 9, 2025
76823be
chore: disable treat warnings as errors
jhub04 Oct 9, 2025
f7e0abb
feat: add methods to start and stop all thrusters
jhub04 Oct 9, 2025
545153c
feat: configure wdt
jhub04 Oct 15, 2025
d89fccf
feat: clear wdt after setting pwms
jhub04 Oct 15, 2025
74b9392
feat: use can0 instead of can1
jhub04 Oct 15, 2025
db500eb
feat: create can rx and tx callbacks for debugging purposes
jhub04 Oct 15, 2025
1c14a23
docs: add TODO reminder to remove CAN callback methods after CAN veri…
jhub04 Oct 15, 2025
c422510
feat: change CAN-FD element size from 8 to 64 bytes
jhub04 Oct 15, 2025
2971c1f
feat: read can messages from Rx FIFO0
jhub04 Oct 15, 2025
2ffb14d
feat: enable WDT
jhub04 Oct 15, 2025
c74949d
feat: configure power manager
jhub04 Oct 19, 2025
c8f1147
feat: enter idle mode in main loop to reduce power consumption
jhub04 Oct 19, 2025
d1dfa88
feat: add CAN message handler
jhub04 Oct 19, 2025
8edf829
feat: add message handler function declaration and refactoring of consts
jhub04 Oct 19, 2025
c929b9e
refactor: make messages_to_read const
jhub04 Oct 19, 2025
02ce9d6
refactor: separate app logic from main.c
jhub04 Oct 19, 2025
f9681c4
feat: make rx and tx callbacks static
jhub04 Oct 19, 2025
ed4a82a
refactor: rename variable name for thruster pwm period
jhub04 Oct 19, 2025
f351a0a
refactor: use NVIC_SystemReset instead of manual wdt reset
jhub04 Oct 19, 2025
5f8db5c
refactor: rename tcc_num to instance
jhub04 Oct 19, 2025
5d4fdc8
feat: set light pwm period to 20000ms
jhub04 Oct 22, 2025
16b9e8b
feat: create method to set light pwm
jhub04 Oct 22, 2025
f94b623
feat: add state set_light_pwm
jhub04 Oct 22, 2025
f7950de
feat: pet watchdog after setting light pwm
jhub04 Oct 22, 2025
ffa5026
refactor: use switch statement instead of hardcoded tcc instance for …
jhub04 Oct 22, 2025
bdbd88a
refactor: rename light to lights
jhub04 Oct 22, 2025
e3ce66b
docs: add TODO reminder to clamp tcc value
jhub04 Oct 22, 2025
cb2eeb0
style: remove leading whitespace
jhub04 Oct 22, 2025
12ddaff
feat: add clamping for thrusters and lights
jhub04 Oct 22, 2025
913a34d
refactor: extract switch for setting tcc into function
jhub04 Oct 22, 2025
fae6149
refactor: rename duty_cycle to us and tcc_value to ticks
jhub04 Oct 22, 2025
a573447
refactor: rename tcc period to tcc period_ticks
jhub04 Oct 22, 2025
14e5b9e
refactor: extract separate function to convert pulse us to ticks
jhub04 Oct 22, 2025
2432bc8
refactor: rename pwm period consts to us for consistent naming
jhub04 Oct 22, 2025
54fd486
refactor: rename us to pulse_us
jhub04 Oct 22, 2025
3707258
fix: stop_thrusters neutralizes thrusters
jhub04 Oct 22, 2025
9369b00
style: use u instead of mu symbol in comments
jhub04 Oct 22, 2025
e444df2
feat: set start_thrusters to no-op
jhub04 Oct 22, 2025
5711f2d
feat: add method to turn lights off
jhub04 Oct 22, 2025
dde5b12
feat: add turn lights off event
jhub04 Oct 22, 2025
86407a0
feat: configure ADC0 with sleep during standby
jhub04 Oct 22, 2025
0939222
feat: set up adc variables
jhub04 Oct 22, 2025
abbee2e
feat: enable ADC0 in app_init
jhub04 Oct 23, 2025
7697731
feat: create method to read thruster's current draw
jhub04 Oct 23, 2025
77528cc
feat: break out of function if overcurrent detected
jhub04 Oct 23, 2025
92adba2
feat: read current draw after each CAN receive
jhub04 Oct 23, 2025
fd7627a
refactor: add function prototype and move read_current_draw
jhub04 Oct 23, 2025
634548a
fix: adc actually converts now
jhub04 Oct 23, 2025
a9f6d14
feat: configure adc0 for adc dma sequencing
jhub04 Oct 23, 2025
907283f
feat: enable result ready input for adc
jhub04 Oct 23, 2025
f992273
feat: configure tc0 witt timer period overflow event enabled
jhub04 Oct 23, 2025
579e2fb
feat: configure dmac channels 0 and 1
jhub04 Oct 23, 2025
7d0ca86
Enable adc input control
jhub04 Oct 26, 2025
8748806
feat: configure evsys user and event channel
jhub04 Oct 26, 2025
6ed4476
feat: enable adc start event input on rising edge
jhub04 Oct 26, 2025
251b089
feat: disable run during standby
jhub04 Oct 26, 2025
f0f56e9
feat: configure adc dma sequencing
jhub04 Oct 26, 2025
4240764
feat: implement minimal overcurrent check
jhub04 Oct 26, 2025
8d1c903
feat: reduce callback time complexity
jhub04 Oct 26, 2025
43f3c4a
refactor: rename variable i to sample
jhub04 Oct 26, 2025
2842d2a
style: use consistent curly bracket indentation
jhub04 Oct 26, 2025
15ece22
refactor: make rated current unsigned
jhub04 Oct 26, 2025
1c31277
feat: add logging
jhub04 Oct 26, 2025
64dd3e9
fix: valid printf
jhub04 Oct 26, 2025
77ba63a
fix: change adc_seq_regs to uint32_t array
jhub04 Oct 26, 2025
b1f810b
feat: add adc average control
jhub04 Oct 26, 2025
2e69f24
feat: enable adc sample averaging
jhub04 Oct 26, 2025
2590b87
chore: remove adc averaging to avoid merge conflicts in the developme…
jhub04 Oct 26, 2025
d3b21fb
refactor: make clamp function easier to read
jhub04 Oct 29, 2025
d995048
refactor: remove unused global variable input_voltage
jhub04 Oct 29, 2025
192f8ea
feat: add realistic values for current monitoring
jhub04 Oct 29, 2025
39cfba2
feat: configure adc pins for testing
jhub04 Oct 29, 2025
d63ce3a
feat: add debugging prints
jhub04 Oct 29, 2025
3849a28
test: current readings on a simple circuit
jhub04 Oct 29, 2025
c18e3bc
feat: configure CAN1
jhub04 Oct 30, 2025
3a2a94a
Reconfigure can states and import some function from demo
jhub04 Oct 30, 2025
14f0327
fix: handle can frames correctly
jhub04 Nov 2, 2025
9ec25d0
feat: implement can id check
jhub04 Nov 2, 2025
c8c5975
feat: improve can receive callback
jhub04 Nov 2, 2025
6a56c90
feat: add debugging prints to can transmit callback
jhub04 Nov 2, 2025
93e4990
refactor: remove unused demo functions
jhub04 Nov 2, 2025
35702b6
style: remove outdated comments
jhub04 Nov 2, 2025
17c7d65
refactor: remove unused functions turn_thrusters_on and turn_lights_on
jhub04 Nov 2, 2025
01ad09b
feat: configure tcc0 pin
jhub04 Nov 2, 2025
66d9704
feat: update can1 configuration
jhub04 Nov 2, 2025
ac7b283
feat: change tcc0/1 period to 75000ms
jhub04 Nov 2, 2025
5169d3a
fix: initialize PWM
jhub04 Nov 2, 2025
7356cc1
feat: only enter message_handler after received flag is true
jhub04 Nov 2, 2025
78fc231
refactor: add debugging statements
jhub04 Nov 2, 2025
64712e9
Set thrusters and lights to neutral on initialization
jhub04 Nov 2, 2025
f08aadc
refactor: check adc_dma_done before entering overcurrent check
jhub04 Nov 2, 2025
5fa600a
Merge branch 'main' into feat/thruster-mcu
jhub04 Nov 2, 2025
327afa9
refactor: use plib function to retrieve pwm period instead of hardcod…
jhub04 Nov 2, 2025
68f1c77
fix: mcc merge conflict
jhub04 Nov 2, 2025
211115f
refactor: call turn_thrusters_off directly instead of setting flag
jhub04 Nov 5, 2025
311ff4d
style: space out switch statement
jhub04 Nov 5, 2025
3f96af0
refactor: remove all printf's
jhub04 Nov 5, 2025
774acc1
revert: use hardcoded values for tccx period
jhub04 Nov 5, 2025
45aefea
refactor: remove unused can state enum
jhub04 Nov 5, 2025
ac54230
feat: configure can to automatically handle id filtering
jhub04 Nov 5, 2025
ca6f4fb
refactor: directly map can states enum to the ID's
jhub04 Nov 5, 2025
5ab88db
refactor: use snake-case for structs and use structs instead of typedefs
jhub04 Nov 5, 2025
ac9fba7
style: more precise comment
jhub04 Nov 5, 2025
78b7342
refactor: remove sys_tasks function in main loop
jhub04 Nov 5, 2025
bd204fb
docs: add Doxygen comments to utility functions
jhub04 Nov 5, 2025
c98c6c1
refactor: rename functions to snake_case for consistency
jhub04 Nov 5, 2025
26c79f8
refactor: move hardware constants to local scope in check_overcurrent()
jhub04 Nov 5, 2025
8e3189c
refactor: move defines after include and before constants
jhub04 Nov 5, 2025
862455d
refactor: combine thruster and light struct into one generic struct
jhub04 Nov 5, 2025
9c5f514
refactor: combine set_thruster_pwm and set_light_pwm into one generic…
jhub04 Nov 5, 2025
0064529
refactor: combine turn_thrusters_off and turn_lights_off into one gen…
jhub04 Nov 5, 2025
55a0079
docs: add TODO reminder to send can transmit on overcurrent fault
jhub04 Nov 5, 2025
0be419e
feat: transmits fault message over CAN if overcurrent flagged
jhub04 Nov 9, 2025
b53c999
fix: initialize txBuffer to txFiFo after zeroing out
jhub04 Nov 10, 2025
f977211
feat: transmit 64 bytes instead of 7 bytes
jhub04 Nov 10, 2025
c907323
refactor: log error if can message transmit fails
jhub04 Nov 10, 2025
5853718
feat: enable can timestamps
jhub04 Nov 10, 2025
a1bc772
fix: read CAN data in little-endian format instead of big-endian
jhub04 Nov 10, 2025
70687f7
refactor: send 8 byte payload instead of 64 bytes
jhub04 Nov 10, 2025
a84dce5
refactor: include min_us, max_us, neutral_us and frame_us as part of …
jhub04 Nov 10, 2025
7707b09
feat!: migrate from SAME54P20A to SAMC21J18A
jhub04 Nov 12, 2025
81b52a7
fix: reenable WDT
jhub04 Nov 12, 2025
268a38b
feat: configure DMAC for adc sleepwalking
jhub04 Nov 12, 2025
0d15d1a
refactor: initialize adc before dma
jhub04 Nov 12, 2025
3581609
feat: add simple adc dma callback
jhub04 Nov 12, 2025
2cbf740
feat: register DMAC callback and transfer protocol
jhub04 Nov 12, 2025
5ddca2f
feat: adc averaging of 1024 samples
jhub04 Nov 12, 2025
1eb1392
feat: configure pins for the SAMC21 Xplained Pro
jhub04 Nov 12, 2025
63c2778
feat: enable starte event input on rising edge
jhub04 Nov 12, 2025
bcde4c1
feat: configure EIC
jhub04 Nov 12, 2025
b4a9e1e
feat: configure eic interrupt line on debboard pin
jhub04 Nov 12, 2025
041ee58
feat: configure ISR for EXTINT0
jhub04 Nov 12, 2025
439fcdf
feat: configure 8 EXTINT channels
jhub04 Nov 12, 2025
a4da8ba
feat: register callback for all EXTINT channels
jhub04 Nov 12, 2025
73d3846
fix: correct function call
jhub04 Nov 12, 2025
d95e772
fix: re-arm DMA for next conversion
jhub04 Nov 12, 2025
e6f6e9e
refactor: uncomment check_overcurrent()
jhub04 Nov 12, 2025
add545d
fix: miscellaneous issues
jhub04 Jan 5, 2026
b177de5
fix: reconfigure dmac
jhub04 Jan 5, 2026
ab6e233
fix: create new project
jhub04 Jan 5, 2026
2c662f1
fix: duplicated project
jhub04 Jan 5, 2026
e1ac622
fix: add app.h include in main.c
jhub04 Jan 5, 2026
3bdf716
refactor: remove comment to remember average sampling
jhub04 Jan 5, 2026
dfedf62
fix: implement correct padding for thruster fault transmission
jhub04 Jan 5, 2026
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
363 changes: 363 additions & 0 deletions thruster_mcu/src/app/app.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,363 @@
#include <string.h>
#include <stdbool.h>
#include <stddef.h>
#include "definitions.h"
#include "app.h"

#define WRITE_ID(id) (id << 18)
#define READ_ID(id) (id >> 18)

/* --- Constants --- */
static const uint32_t TCC0_PERIOD = 75000U;
static const uint32_t TCC1_PERIOD = 75000U;
static const uint32_t TCC2_PERIOD = 18500U;
static const uint32_t THRUSTER_PWM_PERIOD_US = 20000U; // 50Hz
static const uint32_t LIGHT_PWM_PERIOD_US = 20000U; // 50Hz

/* --- Types --- */
struct pwm_output {
uint8_t instance;
uint8_t channel;
uint32_t period_ticks;
};

enum can_events {
TURN_THRUSTERS_OFF = 0x369,
TURN_LIGHTS_OFF = 0x36A,
RESET = 0x36B,
SET_THRUSTER_PWM = 0x36C,
SET_LIGHT_PWM = 0x36D
};

/* --- Private states --- */
/* CAN */
static uint8_t Can1MessageRAM[CAN1_MESSAGE_RAM_CONFIG_SIZE] __attribute__((aligned(32)));

static volatile uint32_t can_status = 0;
static volatile bool can_message_received = false;

static uint8_t txFiFo[CAN1_TX_FIFO_BUFFER_SIZE];
static uint8_t rxFiFo0[CAN1_RX_FIFO0_SIZE];

/* ADC */
static const uint32_t adc_seq_regs[8] = {0x1801, 0x1802, 0x1803, 0x1805, 0x1806, 0x1807, 0x1812, 0x1813};

static volatile uint16_t adc_res[8] = {0};
static volatile bool adc_dma_done = false;

/* Application */
static struct pwm_output thrusters[8] = {
{0, 0, TCC0_PERIOD}, // TCC0_CHANNEL0
{0, 1, TCC0_PERIOD}, // TCC0_CHANNEL1
{0, 2, TCC0_PERIOD}, // TCC0_CHANNEL2
{0, 3, TCC0_PERIOD}, // TCC0_CHANNEL3
{0, 4, TCC0_PERIOD}, // TCC0_CHANNEL4
{0, 5, TCC0_PERIOD}, // TCC0_CHANNEL5
{1, 0, TCC1_PERIOD}, // TCC1_CHANNEL0
{1, 1, TCC1_PERIOD} // TCC1_CHANNEL1
};

static struct pwm_output lights[1] = {{1, 2, TCC1_PERIOD}}; // TCC1_CHANNEL2

/* --- Private function prototypes --- */

/**
* @brief Sets PWM pulse widths for multiple PWM outputs (e.g Thrusters and/or lights).
*
* Parses the data buffer containing pulse width values,
* clamps each to the valid range, and updates the corresponding PWM channels.
*
* @param data Pointer to buffer containing pulse widths in microseconds (format: [MSB, LSB] per output)
* @param outputs Pointer to array of pwm_output structs
* @param count Number of outputs to set
* @param min_us Minimum pulse width in microseconds
* @param max_us Maximum pulse width in microseconds
* @param frame_us Total PWM frame duration in microseconds
*/
static void set_pwm_outputs(const uint8_t *data, struct pwm_output *outputs, size_t count, uint16_t min_us, uint16_t max_us, uint32_t frame_us);

/**
* @brief Handles incoming CAN messages and dispatches them to their corresponding action.
*/
static void message_handler(void);

/**
* @brief Monitors thruster current draw and shuts down on overcurrent condition.
*
* Reads ADC samples for all 8 thruster channels, calculates output current from
* the voltage, and disables all thrusters if any channel exceeds the rated current limit.
*/
static void check_overcurrent(void);

/**
* @brief Sends an overcurrent fault message over CAN
*
* Constructs and transmits a 7-byte CAN FD fault message containing diagnostic information about the fault condition.
*
* @param thruster_id Thruster identifier (0-7)
* @param current Measured current value in Amperes
* @param adc_raw Raw ADC reading
* @return true if message was transmitted successfully, false if not
*/
static bool send_thruster_fault(uint8_t thruster_id, float current, uint16_t adc_raw);

/**
* @brief Sets PWM outputs to their neutral/off position
*
* @param outputs Pointer to array of pwm_output structs
* @param count Number of outputs to set
* @param neutral_us Neutral pulse width in microseconds
* @param frame_us Total PWM frame duration in microseconds
*/
static void set_pwm_neutral(struct pwm_output *outputs, size_t count, uint16_t neutral_us, uint32_t frame_us);

/**
* @brief Clamps a value between a minimum and maximum bound.
*
* @param value The value to clamp
* @param low The lower bound (inclusive)
* @param high The higher bound (inclusive)
* @return The clamped value: low if value < low, high if value > high, otherwise value
*/
static inline uint16_t clamp(uint16_t value, uint16_t low, uint16_t high);

/**
* @brief Write PWM duty cycle to the specified TCC instance and channel.
*
* Routes the PWM write to the appropriate TCC peripheral based on instance number.
*
* @param instance TCC instance number (0, 1 or 2)
* @param channel PWM channel number within the instance
* @param ticks Duty cycle value in timer ticks
*/
static inline void tcc_write(uint8_t instance, uint8_t channel, uint32_t ticks);

/**
* @brief Converts a pulse width in microseconds to timer ticks.
*
* Calculates the timer tick count needed to produce a specific pulse width
* based on the timer's period and the PWM frame duration.
*
* @param period_ticks Timer period in ticks
* @param pulse_us Desired pulse width in microseconds
* @param frame_us Total PWM frame duration in microseconds
* @return Number of timer ticks corresponding to the pulse width
*/
static inline uint32_t us_to_ticks(uint32_t period_ticks, uint16_t pulse_us, uint32_t frame_us);

/* Callbacks */
static void can_receive_callback(uint8_t numberOfMessage, uintptr_t context);
static void can_transmit_callback(uintptr_t context);

static void adc_sram_dma_callback(DMAC_TRANSFER_EVENT event, uintptr_t contextHandle);

/* --- Public functions --- */

void app_init(void) {
// Configure CAN RAM & callbacks
CAN1_MessageRAMConfigSet(Can1MessageRAM);
CAN1_RxFifoCallbackRegister(CAN_RX_FIFO_0, can_receive_callback, (uintptr_t)NULL);
CAN1_TxFifoCallbackRegister(can_transmit_callback, (uintptr_t)NULL);

// Configure DMA
DMAC_ChannelCallbackRegister(DMAC_CHANNEL_1, adc_sram_dma_callback, 0);
DMAC_ChannelTransfer(DMAC_CHANNEL_1, (const void *)&ADC0_REGS->ADC_RESULT, (const void *)adc_res, 16); // Each adc result is 16 bits=2 bytes. 8*2=16
DMAC_ChannelTransfer(DMAC_CHANNEL_0, (const void *)adc_seq_regs, (const void *)&ADC0_REGS->ADC_DSEQDATA, 32); // DSEQDATA is 32 bits=4 bytes. 8 * 4 = 32

TCC0_PWMStart();
TCC1_PWMStart();
//TCC2_PWMStart();

// Set all thrusters and lights to neutral on startup
set_pwm_neutral(thrusters, 8, 1500, THRUSTER_PWM_PERIOD_US);
set_pwm_neutral(lights, 1, 1100, LIGHT_PWM_PERIOD_US);


ADC0_Enable(); // TODO: Remember to manually configure sample averaging in plib_adc0 before testing

TC0_TimerStart();

// Enable watchdog
WDT_Enable();
}

void app_task(void) {
if (adc_dma_done) {
adc_dma_done = false;
check_overcurrent();
}

if (can_message_received) {
can_message_received = false;
message_handler();
}
}

/* --- Private helpers --- */

static void message_handler(void) {
// Interpret event from CAN frame id
CAN_RX_BUFFER *rxBuf = (CAN_RX_BUFFER *)rxFiFo0;

uint32_t id = rxBuf->xtd ? rxBuf->id : READ_ID(rxBuf->id);
const uint8_t *pData = rxBuf->data;

switch (id) {
case TURN_THRUSTERS_OFF:
set_pwm_neutral(thrusters, 8, 1500, THRUSTER_PWM_PERIOD_US);
break;

case TURN_LIGHTS_OFF:
set_pwm_neutral(lights, 1, 1100, LIGHT_PWM_PERIOD_US);
break;

case RESET:
/* Force a system reset */
NVIC_SystemReset();
break;

case SET_THRUSTER_PWM:
set_pwm_outputs(pData, thrusters, 8, 1000, 2000, THRUSTER_PWM_PERIOD_US);
break;

case SET_LIGHT_PWM:
set_pwm_outputs(pData, lights, 1, 1100, 1900, LIGHT_PWM_PERIOD_US);
break;

default:
/* Unknown event: ignore */
break;
}
}

static void check_overcurrent(void) {
const float ADC_VREF = 3.3f;
const float G_IMON = 18.18e-6f; // Amplifier gain 18.18 uA/A -> in A/A
const float R_IMON = 2550.0f; // 2.55 kilo ohms resistor
const uint8_t THRUSTER_RATED_CURRENT = 15U; // From TSD7 datasheet

for (size_t sample = 0; sample < 8; sample++) {
float V_Imon = (float)adc_res[sample] * ADC_VREF / 4095.0f;
float I_out = V_Imon / (G_IMON * R_IMON);

//printf("raw=%u V_Imon=%.4f V I_out=%.3f A\r\n",(unsigned)adc_res[sample], (double)((float)adc_res[sample]*ADC_VREF/4095.0f), (double)I_out);
if (I_out > THRUSTER_RATED_CURRENT) {
set_pwm_neutral(thrusters, 8, 1500, THRUSTER_PWM_PERIOD_US);

if (!send_thruster_fault(sample, I_out, adc_res[sample])) {
// Handle retransmission?
}
break;
}
}
}

static bool send_thruster_fault(uint8_t thruster_id, float current, uint16_t adc_raw) {
CAN_TX_BUFFER *txBuffer = NULL;

memset(txFiFo, 0x00, CAN1_TX_FIFO_BUFFER_SIZE);
txBuffer->id = WRITE_ID(0x45A); // Just a random ID
txBuffer->dlc = 0x7U; // DLC 7 -> 7 Byte Payload
txBuffer->fdf = 1;
txBuffer->brs = 1;

txBuffer->data[0] = thruster_id;
memcpy(&txBuffer->data[1], &current, sizeof(float));
memcpy(&txBuffer->data[5], &adc_raw, sizeof(uint16_t));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Be careful here, you are currently sending the data in packed format. This reduces the total size but makes it harder to parse later since the data is unalligned. This is something i am currently dealing a lot with on the MRU and something we should avoid here. Add padding to make sure that the data is alligned correctly

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay I will start by just sending 64 bytes with a lot of excessive padding, and then I can cut down on that when I get it working.

Copy link
Contributor

@forrisdahl forrisdahl Nov 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The important padding here is the padding between the different elements

  • uint16_t must be 2-byte aligned
  • float must be 4-byte aligned

This affects how the compiler inserts padding to ensure proper alignment.

For more detail on structure padding and alignment, see: https://www.geeksforgeeks.org/c/structure-member-alignment-padding-and-data-packing/

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be done now:)


return CAN1_MessageTransmitFifo(1, txBuffer);
}


static void set_pwm_outputs(const uint8_t *data, struct pwm_output *outputs, size_t count, uint16_t min_us, uint16_t max_us, uint32_t frame_us) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we are moving to SAMC21 TCC2 might have different min_us and max_us than TCC0 and TCC1. Consider adding an additional field in the struct containing the min and max value since these are constants defined at compile time

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to clarify, are you anticipating that TCC2 will have different hardware imposed min/max pulse widths than TCC0/TCC1 on the SAMC21? Or is this just for general flexibility?

Copy link
Contributor

@forrisdahl forrisdahl Nov 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, but I think currently this function has more inputs that we need. We know the min,max CC values on compile time so it would make sense to include it as a part of the thruster table.

I think when I commented it I thought that min_us was the tcc value and not the time in us.

for (size_t i = 0; i < count; i++) {
uint16_t pulse_us = ((uint16_t)data[2U * i] << 8) | (uint16_t)data[2U * i + 1U];

pulse_us = clamp(pulse_us, min_us, max_us);

uint32_t ticks = us_to_ticks(outputs[i].period_ticks, pulse_us, frame_us);

tcc_write(outputs[i].instance, outputs[i].channel, ticks);
}

// Pet the watchdog after applying updates
WDT_Clear();
}

static void set_pwm_neutral(struct pwm_output *outputs, size_t count, uint16_t neutral_us, uint32_t frame_us) {
for (size_t i = 0; i < count; i++) {
uint32_t ticks = us_to_ticks(outputs[i].period_ticks, neutral_us, frame_us);
tcc_write(outputs[i].instance, outputs[i].channel, ticks);

}
WDT_Clear();
}

static inline uint16_t clamp(uint16_t value, uint16_t low, uint16_t high) {
if (value < low) {
return low;
} else if (value > high) {
return high;
} else {
return value;
}

}

static inline void tcc_write(uint8_t instance, uint8_t channel, uint32_t ticks) {
switch (instance) {
case 0:
TCC0_PWM24bitDutySet(channel, ticks);
break;

case 1:
TCC1_PWM24bitDutySet(channel, ticks);
break;

case 2:
TCC2_PWM16bitDutySet(channel, (uint16_t)ticks);
break; // Not used with current mapping

default:
break;
}
}

static inline uint32_t us_to_ticks(uint32_t period_ticks, uint16_t pulse_us, uint32_t frame_us) {
return ((uint32_t)pulse_us * (period_ticks + 1U)) / frame_us;
}

static void can_receive_callback(uint8_t numberOfMessage, uintptr_t context) {
// Check CAN Status
can_status = CAN1_ErrorGet();

// If no new error, handle CAN frame
if (((can_status & CAN_PSR_LEC_Msk) == CAN_ERROR_NONE) ||
((can_status & CAN_PSR_LEC_Msk) == CAN_ERROR_LEC_NC)) {

memset(rxFiFo0, 0x00, (numberOfMessage * CAN1_RX_FIFO0_ELEMENT_SIZE));
if (CAN1_MessageReceiveFifo(CAN_RX_FIFO_0, numberOfMessage, (CAN_RX_BUFFER *)rxFiFo0) == true) {
can_message_received = true;
// Optionally print can frame
}
}
}

static void can_transmit_callback(uintptr_t context) {
// Check CAN Status
can_status = CAN1_ErrorGet();

if (((can_status & CAN_PSR_LEC_Msk) == CAN_ERROR_NONE) ||
((can_status & CAN_PSR_LEC_Msk) == CAN_ERROR_LEC_NC)) {
//printf("CAN TX successful\r\n");
}
}

static void adc_sram_dma_callback(DMAC_TRANSFER_EVENT event, uintptr_t contextHandle) {

if (event == DMAC_TRANSFER_EVENT_COMPLETE) {
adc_dma_done = true;
}
}


23 changes: 23 additions & 0 deletions thruster_mcu/src/app/app.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#ifndef APP_H
#define APP_H




/* Provide C++ Compatibility */
#ifdef __cplusplus
extern "C" {
#endif

/* One-time initialization for this module */
void app_init(void);

/* Handle incoming frames and actions */
void app_task(void);


#ifdef __cplusplus
}
#endif

#endif
Loading