Device mockups overview - PW-Sat2/PWSat2OBC GitHub Wiki
--------- ----------------------------- ----------
| | I2C | | USB | |
| OBC | <======> | DeviceMock | <======> | PC |
| | || | | | |
--------- || ----------------------------- ----------
||
vv
------------
| |
| Logic |
| Analyzer |
| |
------------
- OBC dev board - EFM32GG990F1024
- DeviceMock
- ATmega88PA
- FT-X
- [Optional] Logic Analyzer (Saleae etc.)
- Mock exposes I2C interface as slave
- Communication with PC is done using serial port (FT-X chip)
_ TODO: Photo _
- H1 - 2x all interfaces (GND, I2C, SPI, 2xCS) for connection with other devices
- H2 - 2x all interfaces (GND, I2C, SPI, 2xCS) for logic analyzer
- H3 - Power (GND, 3.3V)
- H4, H5 - GPIO
- H6 - Serial port lines (TXD, RXD, CTS, RTS)
AVR acts as a catch-all slave device. Relaying on I2C clock streching is communicates with PC using UART. Software on PC can interpret and prepare response that will be sent back to master device. It is also possible to cause some faults on I2C bus like disabling address acknowledge (resulting in NAK) or freezing (resulting in SCL latched at low level).
PC is communicating with DeviceMock using serial port (baudrate 100000, RTS/CTS flow control) with protocol based on commands.
Each command send from DeviceMock to PC is in format:
1 byte | 1 byte | DataLength bytes |
---|---|---|
Command Code | DataLength | Data |
Commands send from PC to DeviceMock uses similar format
1 byte | 1 byte | 1 byte | DataLength bytes |
---|---|---|---|
S |
Command Code | DataLength | Data |
To allow recovery from invalid state (like partially received command) extra start character is needed (S
) at the begining of command. Also each S
in size/data needs to be escaped by prepending it with another S
(e.g. XSZ
should be sent as XSSZ
).
Direction | Name | Code | Data |
---|---|---|---|
Mock -> PC | Version | 0x1 |
Version |
Mock -> PC | Write | 0x2 |
Address Data
|
PC -> Mock | ReadResponse | 0x3 |
Data |
PC -> Mock | I2C Disable | 0x4 |
None |
PC -> Mock | I2C Enable | 0x5 |
None |
PC -> Mock | Restart | 0x6 |
None |
PC -> Mock | I2C Free | 0x7 |
None |
PC -> Mock | Stop | 0x8 |
None |
Mock -> PC | Stopped | 0x9 |
None |
I2C write-read transfer is composed by few steps:
I2C bus | DeviceMock action | PC action |
---|---|---|
Write address issued | Data collecting start | |
N-bytes received | Data is being accumulated | |
STOP condition | Data is sent to PC | Data is received and response is generated |
Read address issues (clock stretching) | Awaiting data | When response is ready, it is sent back to Mock |
Clock stretching | Response received from PC | |
N-bytes written | Data is sent byte-by-byte to I2C |
Usage of DeviceMock allows easy mocking of I2C devices. Actual implementation of all devices are handled with Python code.
Each device is represented by subclass for I2CDevice
class. Writes to that devices are translated to calls of methods decorated with i2cMock.command
. Parameter passed to that decorator are used to determine which method should be called. For example, given following methods:
@i2cMock.command([0x01, 0x02])
def method_1(a, b):
pass
@i2cMock.command([0x03])
def method_2(a, *args):
pass
transfers will result in:
- [0x01, 0x02, 0xA0, 0xB0] ->
method1(0xA0, 0xB0)
- [0x03, 0xAB, 0x10, 0x20, 0x30] ->
method2(0xAB, (0x20, 0x30))
Return value of each method must be None
or byte array and will be sent to master in next read transfer
Mock is controled using I2CMock
class which handles all details of communication protocol. After creation, objects handling each supported devices should be added using method add_device
. It is possible to share the same object between mocks.
When mock is configured, background thread can be started by calling start
method. This also ensures clean state of mock and enables I2C bus.
It is possible to disable I2C slave address acknowledge by calling disable
method. Device can freeze itself using freeze
method. Recovery from that situation is possible by using unfreeze
method of I2CMock
class