Project repo: https://github.com/UWARG/efs-can-power-module
Specifications (from the EE Team)
Overall Hardware Architecture (taken from https://uwarg-docs.atlassian.net/wiki/x/AoCsmQ )
Inc drawio | ||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
Background
The drone uses a battery pack containing 12 cells, where 2 groups of 6 cells are each connected in series (hence 6S
).
Each group of 6 cells is connected to a battery monitoring circuit (shortened as BMC later).
This circuit measures:
The voltage of each cell
The current delivered by the group
This is done by forcing the battery to deliver current through a resistor with a tiny resistance. This resistor, called a shunt/shunt resistor, follows Ohm’s law:
V=IR
This implies that
I=V/R
, which means that the current passing through a shunt is equal to its voltage (which can be measured easily), divided by its resistance (which is a known constant).
And it sends:
Note |
---|
We are not exactly sure of everything yet. |
Battery-related information relating to the voltages and currents
The voltage and current information are sent as 2 analog signals, which our microcontroller converts to digital ones via an Analog-to-Digital Converter (ADC).
Some other information is sent via the I2C protocol
Description
Info |
---|
|
Expand | ||
---|---|---|
| ||
The FC needs this information to make other decisions, e.g. if a part is consuming too much power, or when to land |
Specific Hardware Information (taken from https://uwarg-docs.atlassian.net/wiki/x/HYBXq)
Info |
---|
The ports we are interested in:
|
...
|
...
|
...
|
...
|
...
|
...
|
\uD83E\uDD14 Current Roadblocks
Note |
---|
Libcanard has a high learning curve. Neither the DroneCAN website nor the Libcanard repo has beginner-friendly tutorials. YouTube videos also won’t help directly |
Tasks Break Down:
✅ Action Items
...
- Initialize the Battery Monitoring Circuit using I2C
- Read battery data using the specified ports
\uD83D\uDEA9 Milestones
- Understand project specifications
- Understand the I2C protocol
- Understand the CAN protocol
- Understand DroneCAN
- etc.
Open Questions
...
Are our speculated ports correct?
...
What is transmitted over I2C?
...
How should the MCU beacon itself to the FC (i.e. so that the FC is aware that the MCU on the power module is there)?
What should be sent over CAN? Possible options:
\uD83D\uDD17 Reference materials
Relevant protocols
I2C: I2C - Inter-Integrated Circuit
I2C is all about registers (i.e. memory that can be accessed by an address)
Each I2C peripheral has its own register map (i.e. what the registers are used for, such as storing the voltage of a cell)
To read data from a peripheral, you send an address to it, and it will respond with data
To write data to a peripheral, you tell it which address to write to, and what data to write
CAN:
A general description of CAN: https://uwarg-docs.atlassian.net/wiki/x/BgDPlw
DroneCAN: a protocol built on top of CAN to communicate information between drone parts and the FC: https://dronecan.github.io/
Libcanard: an implementation of DroneCAN for low-end microcontrollers. An example code on how to use it: https://github.com/dronecan/libcanard/blob/master/examples/SimpleNode/simple_node.c
EFS-Canard: a portable WARG version of the Libcanard library
AP-Periph has some example code on how to send DroneCAN messages when you have a specific type of device (e.g. a battery)
A helpful presentation on CAN: https://docs.google.com/presentation/d/12i6TiUuXQw5mO2u3pq8DMXTD1bCRe8rJtzVZRk6pDz0/edit?usp=sharing
A helpful document on CAN applications: https://uwarg-docs.atlassian.net/wiki/x/AwA8q
Project Resources
Battery Monitoring Circuit
Datasheet: https://www.ti.com/product/BQ76925?qgpn=bq76925#software-development
Addressing: all I2C addresses are calculated using the formula
Code Block language c ADDRESS = 0x20 + REG_ADDR
Where ADDRESS
is the real address to read/write to, and REG_ADDR
is the register address mentioned on the datasheet, from 0x00
to 0x1F
.
Remember this distinction between the real and register addresses. All I2C addresses you see after this point will be register addresses.
Source: Datasheet, 8.5.1.1 I2C Addressing
Selecting the cell for the
VCOUT
pin:
To select the cell to measure the voltage of:Code Block language c // Write to the register address: REG_ADDR = 0x01 // With data: DATA = 0x10 + CELL
Where CELL
is the cell number. CELL=0x0
refers to cell 1, CELL=0x1
refers to cell 2, and so on, up to CELL=0x5
referring to cell 6.
Source: Datasheet, 8.6.1 Register Descriptions (Tables 6 to 8)
Reference voltage selection:
Code Block language c // Write to the register address: REG_ADDR = 0x04 // With data: DATA = REF_SEL
Where REF_SEL
can be set to 0
or 1
, with the following effects on the BMC pins:
Table 14. Reference Voltage Selection
REF_SEL | VREF (V) | VCOUT Gain (V) | VIOUT Voltage Range (V) |
---|---|---|---|
0 | 1.5 | 0.3 | 0.25 - 1.25 |
1 | 3.0 | 0.6 | 0.5 - 2.5 |
Expand | ||
---|---|---|
| ||
Yes, it’s true that you could also enable CRC with this memory address. To do that, add We haven’t decided whether we want CRC though, so right now we’ll use the default option… which is “disabled“. |
Source: Datasheet, 8.6.1 Register Descriptions (Tables 13 and 14)
Cell Voltage Monitoring
Code Block language c // The voltage of the currently selected cell, VCn, // is calculated with the following formula: VCn = ((VCOUT * GC_VREF + OC_VCOUT) / G_VCOUT) * (1 + GC_VCOUT) // Where: VCOUT = ADC_count / Full_scale_count * VREF_NOMINAL // This is the voltage of the VCOUT pin, as measured by the MCU GC_VCOUT = ((VCn_GC_4 << 4) + VCn_GAIN_CORR) * 1E-3 OC_VCOUT = ((VCn_OC_4 << 4) + VCn_OFFSET_CORR) * 1E-3 GC_VREF = (1 + ((VREF_GC_4 << 4) + VREF_GAIN_CORR) * 1E-3) + (((VREF_OC_5 << 5) + (VREF_OC_4 << 4) + VREF_OFFSET_CORR) * 1E-3)/VREF_NOMINAL
Several values can be accessed through the I2C interface:
VCn_GC_4
, wheren
is the cell numberVC1_GC_4
I2C register address:
0x17
Data bit location:
6
VC2_GC_4
I2C register address:
0x17
Data bit location:
4
VC3_GC_4
I2C register address:
0x18
Data bit location:
6
VC4_GC_4
I2C register address:
0x18
Data bit location:
4
VC5_GC_4
I2C register address:
0x18
Data bit location:
2
VC6_GC_4
I2C register address:
0x18
Data bit location:
0
VCn_OFFSET_CORR
,
wheren
is the cell numberVC1_OFFSET_CORR
I2C register address:
0x11
Data bit locations:
7 to 4
VC2_OFFSET_CORR
I2C register address:
0x12
Data bit locations:
7 to 4
VC3_OFFSET_CORR
I2C register address:
0x13
Data bit locations:
7 to 4
VC4_OFFSET_CORR
I2C register address:
0x14
Data bit locations:
7 to 4
VC5_OFFSET_CORR
I2C register address:
0x15
Data bit locations:
7 to 4
VC6_OFFSET_CORR
I2C register address:
0x16
Data bit locations:
7 to 4
VCn_OC_4
, wheren
is the cell numberVC1_OC_4
I2C register address:
0x17
Data bit location:
7
VC2_OC_4
I2C register address:
0x17
Data bit location:
5
VC3_OC_4
I2C register address:
0x18
Data bit location:
7
VC4_OC_4
I2C register address:
0x18
Data bit location:
5
VC5_OC_4
I2C register address:
0x18
Data bit location:
3
VC6_OC_4
I2C register address:
0x18
Data bit location:
1
VCn_GAIN_CORR
,
wheren
is the cell numberVC1_GAIN_CORR
I2C register address:
0x11
Data bit locations:
3 to 0
VC2_GAIN_CORR
I2C register address:
0x12
Data bit locations:
3 to 0
VC3_GAIN_CORR
I2C register address:
0x13
Data bit locations:
3 to 0
VC4_GAIN_CORR
I2C register address:
0x14
Data bit locations:
3 to 0
VC5_GAIN_CORR
I2C register address:
0x15
Data bit locations:
3 to 0
VC6_GAIN_CORR
I2C register address:
0x16
Data bit locations:
3 to 0
VREF_OC_5
I2C register address:
0x1b
Data bit location:
2
VREF_OC_4
I2C register address:
0x1b
Data bit location:
1
VREF_GC_4
I2C register address:
0x1b
Data bit location:
0
Source: Datasheet, 8.3.2.2 Cell Voltage Monitoring
Up-to-date electrical schematics from the EE team
Project 6s Power Module (Ask the EE team for access)
Receiving incoming messages (Polling/Interrupt/DMA)
We are going to receive messages from an external device (the BMC), but we can’t predict when we will receive them. There are 3 solutions to this:
Polling: repeatedly checking whether a message was received using a loop. If a message gets detected, it gets processed, then the loop continues. This is a purely software solution.
Advantages:
Very easy to implement
You don’t need to worry about race conditions (when multiple pieces of code unintentionally access and modify some variable at the same time)
Disadvantages:
Extremely inefficient. The loop eats up most of the microcontroller’s computational resources, which it needs for many other tasks.
Interrupt: when a message gets received, it triggers a hardware interrupt. This causes all other processes to stop. Then the message gets processed, and the other processes resume after that. STM32 microcontrollers support many internal interrupts (for common events like receiving an I2C message), and external interrupts (triggered by a GPIO pin).
Advantages:
More efficient than polling
Disadvantages:
You need to be careful about race conditions
Still needs the microcontroller for data reception
Direct Memory Access (DMA): Similar to an interrupt, except the peripheral writes its message directly to the microcontroller’s memory. Then it signals the microcontroller that a new message came.
Advantages:
Most efficient for large messages
Disadvantages:
You need to be careful about race conditions
Less efficient than regular interrupts for small messages