-
Notifications
You must be signed in to change notification settings - Fork 3
Onboarding Task
Not a comprehensive guide to ChibiOS, or embedded development, hopefully just a step-by-step guide that gives you a vague idea of how things work.
TODO I'm not even sure what the actual process is rn, I just wing it :P
Take a look around the repo, and just see where things are, if it doesn't make sense, just take note of it for now.
Read through blinky code and try and understand what's going on.
(Start with blinky/src/main.c
).
Take a peek through the ChibiOS documentation, try and find some of the functions that we're using.
Peek through STM32F303 reference manual (make very clear that you don't need to read the thing cover-to-cover, just like, read GPIO or something).
Copy blinky to new folder named <yourname>_onboarding
.
Explain how to copy/set VSCode configurations (using c_cpp_properties.py script and launch.json)
Start in cfg
.
Edit cfg/halconf.h
Change HAL_USE_ADC to TRUE
#define HAL_USE_ADC TRUE
(Explain here what this does, it enables the ADC HAL module so everything gets built)
Edit cfg/mcuconf.h
Change STM32_ADC_USE_ADC1 to TRUE
(Explain what this does, it creates the ADC1 object or whatever)
Edit src/main.c
Add the following:
static struct hal_adc_config adc1_config = {
.difsel = 0,
};
static ADCConversionGroup measure_conversion = {
.circular = false,
.num_channels = 1,
.end_cb = NULL,
.error_cb = NULL,
.cfgr = 0,
.tr1 = 0,
.smpr = {0b111 << ADC_SMPR1_SMP1_Pos, 0},
.sqr = {1 << ADC_SQR1_SQ1_Pos, 0, 0, 0},
};
ADC configuration is needed for device-specific stuff. (ChibiOS can't encapsulate everything)
Not strictly necessary here, as the default is exactly what we're doing here.
DIFSEL is all 0's so all the ADC inputs are single-ended (i.e. referenced to ground)
Maybe explain how we know what the struct looks like (alternative is clicking through the defines)
- ADCConfig: ChibiOS/os/hal/include/hal_adc.h
- adc_lld_config_fields: somewhere in ports?
- check ChibiOS/os/hal/ports/STM32
- ChibiOS/os/hal/ports/STM32/STM32F3xx for the F303
- Check platform.mk, will include different versions of ADC driver
- Specifically include $(CHIBIOS)/os/hal/ports/STM32/LLD/ADCv3/driver.mk
- Now can look in that folder, and see what adc_lld_config_fields is defined as
- This can also be done just by clicking through in a properly configured VSCode
ADCConversionGroup is similar, except here there's some common configuration to set as well
Common stuff:
- Circular mode means it will keep starting over again. We want a single reading.
- We only want to use 1 ADC channel (measure 1 pin)
- No ending callback, we're waiting for it to finish
- No error callback, assuming no errors - we aren't doing anything fancy
Peripheral-specific:
- default CFGR is fine, take a look at the RM
- No threshold value to set, this is a special feature of this ADC, see RM if curious
- Set the highest sample time for the most accurate value on channel 1
- Use channel 1 as first capture in sequence - length is set by driver, so we can ignore setting that
Add the following after starting heartbeat thread:
palSetPadMode(GPIOA, 0, PAL_MODE_INPUT_ANALOG);
adcStart(&ADCD1, &adc1_config);
Add a new thread:
static THD_WORKING_AREA(waAdcThread, 256);
static THD_FUNCTION(AdcThread, arg) {
(void)arg;
while (1) {
adcsample_t val;
msg_t rc = adcConvert(&ADCD1, &measure_conversion, &val, 1);
if (rc == MSG_OK) {
float voltage = ((float) val / 4096) * 3.3;
} else {
// Should never get here
chSysHalt("ADC Error - should be unreachable");
}
}
}
Explain that adcConvert starts a conversion and waits for it to finish. Perhaps explain async functions as well.
Then explain how voltage is calculated.
Start the thread in main after configuration:
palSetPadMode(GPIOA, 0, PAL_MODE_INPUT_ANALOG);
adcStart(&ADCD1, &adc1_config);
chThdCreateStatic(waAdcThread, sizeof(waAdcThread), NORMALPRIO,
AdcThread, NULL);
Sets pad mode to an analog input - GPIO has to be configured correctly
so that different peripherals can use them. There is a default configuration
in board.h
in the SPEEDY_CONFIG_F3 folder, but it doesn't include this one.
Then we start ADC thread, and give it access to the waAdcThread, where the stack is placed. (maybe brief overview/reminder of stack?)
Hijacking a v0 message type, situation should be improved with v1, but for now making new types is annoying and out of scope
include message type:
include "uavcan/equipment/power/CircuitStatus.h"
Now back in AdcThread:
extern CanardInstance canard_instance; // Code smell, this should be wrapped with a mutex of some sort
static THD_WORKING_AREA(waAdcThread, 256);
static THD_FUNCTION(AdcThread, arg) {
(void)arg;
uint8_t transfer_id = 0;
while (1) {
adcsample_t val;
msg_t rc = adcConvert(&ADCD1, &measure_conversion, &val, 1);
if (rc == MSG_OK) {
float voltage = ((float) val / 4096) * 3.3;
uint8_t msg_buf[UAVCAN_EQUIPMENT_POWER_CIRCUITSTATUS_MAX_SIZE];
uavcan_equipment_power_CircuitStatus status = {
.circuit_id = 0,
.voltage = voltage,
.current = 0,
.error_flags = 0,
};
uint32_t len = uavcan_equipment_power_CircuitStatus_encode(&status, (void *)msg_buf);
canardBroadcast(&canard_instance,
UAVCAN_EQUIPMENT_POWER_CIRCUITSTATUS_SIGNATURE,
UAVCAN_EQUIPMENT_POWER_CIRCUITSTATUS_ID,
&transfer_id,
0,
(const void *)msg_buf,
len);
transfer_id += 1;
} else {
// Should never get here
chSysHalt("ADC Error - should be unreachable");
}
}
}
Explain we need to get canard_instance
from somewhere, and that this is actually bad and there should be
wrappers around it.
Explain transfer_id, and how it needs to be static and monotonic.
Message buffer of maximum message size, then create the message. Use the generated encode function
to serialize into that buffer (saving the length).
Finally, broadcast message with signature and ID. Note that this just enques message onto a list,
transmission will be handled in can_handle_forever
.
TODO
But generally I want to modify it so the blinky does a quick blink, one for each node it sees a Heartbeat from.
This will need:
- Heartbeat subscription
- message passing into heartbeat thread (probably just a global var that hearbeat only reads from)
- probably want to introduce concept of message passing (mailboxes, events, etc.)
- Little list structure to hold up to N node IDs (maybe all 127?)
- Basic blinky logic