Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[communication] Revive Asynchronous Multi-Node Bus (AMNB) protocol #395

Merged
merged 2 commits into from
Jul 31, 2020
Merged
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
132 changes: 132 additions & 0 deletions examples/nucleo_g071rb/amnb/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* Copyright (c) 2019, Niklas Hauser
*
* This file is part of the modm project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

#include <modm/board.hpp>
#include <modm/communication/amnb.hpp>

using namespace Board;
using namespace std::chrono_literals;
using namespace modm::amnb;
// ----------------------------------------------------------------------------

const Listener listeners[] =
{
{1, +[](uint8_t sender, const uint32_t& data)
{
MODM_LOG_INFO << "Node2 and Node3 received Broadcast 1 from '" << sender;
MODM_LOG_INFO << "': " << data << modm::endl;
}
},
{2, [](uint8_t sender)
{
MODM_LOG_INFO << "Node2 and Node3 received Broadcast 2 from '" << sender << "'" << modm::endl;
}
},
};
const Action actions[] =
{
{1, +[]() -> Response
{
static uint8_t counter{0};
MODM_LOG_INFO << "Node1 and Node3 received Action 1" << modm::endl;
return counter++;
}
},
{2, +[](const uint32_t& data) -> Response
{
static uint8_t counter{0};
MODM_LOG_INFO << "Node1 and Node3 received Action 2 with argument: " << data << modm::endl;
return ErrorResponse(counter++);
}
},
};

// Two nodes on the same device on different UARTs of course!
DeviceWrapper<Usart1> device1;
DeviceWrapper<Usart3> device2;
DeviceWrapper<Usart4> device3;
Node node1(device1, 1, actions);
Node node2(device2, 2, listeners);
Node node3(device3, 3, actions, listeners);

// You need to connect D1 with D15 and with A0
using PinNode1 = GpioC4; // D1
using PinNode2 = GpioB8; // D15
using PinNode3 = GpioA0; // A0

// ----------------------------------------------------------------------------
int
main()
{
Board::initialize();
LedD13::setOutput();

Usart1::connect<PinNode1::Tx>();
Usart1::initialize<SystemClock, 115200>();
// Use Single-Wire Half-Duplex Mode
PinNode1::configure(Gpio::OutputType::OpenDrain);
PinNode1::configure(Gpio::InputType::PullUp);
USART1->CR1 &= ~USART_CR1_UE;
USART1->CR3 = USART_CR3_HDSEL;
USART1->CR1 |= USART_CR1_UE;

Usart3::connect<PinNode2::Tx>();
Usart3::initialize<SystemClock, 115200>();
// Use Single-Wire Half-Duplex Mode
PinNode2::configure(Gpio::OutputType::OpenDrain);
PinNode2::configure(Gpio::InputType::PullUp);
USART3->CR1 &= ~USART_CR1_UE;
USART3->CR3 = USART_CR3_HDSEL;
USART3->CR1 |= USART_CR1_UE;

Usart4::connect<PinNode3::Tx>();
Usart4::initialize<SystemClock, 115200>();
// Use Single-Wire Half-Duplex Mode
PinNode3::configure(Gpio::OutputType::OpenDrain);
PinNode3::configure(Gpio::InputType::PullUp);
USART4->CR1 &= ~USART_CR1_UE;
USART4->CR3 = USART_CR3_HDSEL;
USART4->CR1 |= USART_CR1_UE;


modm::ShortPeriodicTimer tmr{1s};
uint32_t counter{0};

while (true)
{
node1.update();
node2.update();
node3.update();

if (tmr.execute())
{
LedD13::toggle();
node1.broadcast(1, counter++);
node3.broadcast(2);

{
modm::ResumableResult< Result<uint8_t> > res{0};
while((res = node2.request<uint8_t>(1, 1)).getState() == modm::rf::Running)
{ node1.update(); node2.update(); node3.update(); }
MODM_LOG_INFO << "Node1 responded with: " << res.getResult().error();
MODM_LOG_INFO << " " << *res.getResult().result() << modm::endl;
}
{
modm::ResumableResult< Result<uint8_t, uint8_t> > res{0};
while((res = node1.request<uint8_t, uint8_t>(3, 2, counter)).getState() == modm::rf::Running)
{ node1.update(); node2.update(); node3.update(); }
MODM_LOG_INFO << "Node3 responded with: " << res.getResult().error();
MODM_LOG_INFO << " " << *res.getResult().resultError() << modm::endl;
}
}
}

return 0;
}
19 changes: 19 additions & 0 deletions examples/nucleo_g071rb/amnb/project.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<library>
<extends>modm:nucleo-g071rb</extends>
<options>
<option name="modm:build:build.path">../../../build/nucleo_g071rb/amnb</option>
<option name="modm:platform:uart:1:buffer.tx">1</option>
<option name="modm:platform:uart:1:buffer.rx">32</option>
<option name="modm:platform:uart:3:buffer.tx">1</option>
<option name="modm:platform:uart:3:buffer.rx">32</option>
<option name="modm:communication:amnb:with_heap">False</option>
</options>
<modules>
<module>modm:platform:gpio</module>
<module>modm:communication:amnb</module>
<module>modm:platform:uart:1</module>
<module>modm:platform:uart:3</module>
<module>modm:platform:uart:4</module>
<module>modm:build:scons</module>
</modules>
</library>
16 changes: 16 additions & 0 deletions src/modm/architecture/interface/uart.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ class Uart : public ::modm::PeripheralDriver
static std::size_t
read(uint8_t *data, std::size_t length);

/// @return the size of the receive FIFO queue.
static std::size_t
receiveBufferSize();

/**
* Empty the receive FIFO queue and hardware buffer.
*
Expand All @@ -135,13 +139,25 @@ class Uart : public ::modm::PeripheralDriver
static std::size_t
discardReceiveBuffer();

/// @return the size of the transmit FIFO queue.
static std::size_t
transmitBufferSize();

/**
* Empty the transmit FIFO queue and hardware buffer.
*
* @return the size of the deleted FIFO queue.
*/
static std::size_t
discardTransmitBuffer();

/// @return `true` if an error occured during any write or read
static bool
hasError();

/// Reset the sticky error indication
static void
clearError();
#endif
};

Expand Down
89 changes: 1 addition & 88 deletions src/modm/communication/amnb.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/*
* Copyright (c) 2010-2011, Fabian Greif
* Copyright (c) 2011-2015, Niklas Hauser
* Copyright (c) 2020, Niklas Hauser
*
* This file is part of the modm project.
*
Expand All @@ -10,90 +9,4 @@
*/
// ----------------------------------------------------------------------------

/**
* @ingroup modm_communication
* @defgroup amnb Asynchronous Multi-Node Bus (AMNB)
*
* @section amnb_intro Introduction
*
* The AMNB (**A**synchronous **M**ulti-**N**ode **B**us) is a
* multi-master bus system, using p-persitent CSMA to send messages.
*
* One bus can be populated with up to 64 nodes. The nodes can be queried for
* data and they will respond like an SAB Slave, and can query data from other
* nodes like an SAB Master, or they can just broadcast a message.
* Each node can listen to all the responses and broadcasts and store that
* information for its purpose.
*
* Action callbacks to query requests can be defined as well as universal
* callbacks to any transmitted messaged (Listener callbacks).
* As an optional advanced feature, error handling callbacks can also be defined,
* which fire if messages have not been able to be sent, or requests timed out
* or misbehaved in other manners, or other nodes query unavailable information.
*
* @section amnb_protocol Protocol
*
* Features:
* - Maximum payload length is 32 byte.
* - CRC8 (1-Wire)
*
* @subsection structure Structure
*
@verbatim
+------+--------+--------+---------+--------------+-----+
| SYNC | LENGTH | HEADER | COMMAND | ... DATA ... | CRC |
+------+--------+--------+---------+--------------+-----+
@endverbatim
*
* - `SYNC` - Synchronization byte (always 0x54)
* - `LENGTH` - Length of the payload (without header, command and CRC byte)
* - `HEADER` - Address of the slave and two flag bits
* - `COMMAND` - Command code
* - `DATA` - Up to 32 byte of payload
* - `CRC` - CRC-8 checksum (iButton)
*
* @subsubsection header Header
*
@verbatim
7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+
| Flags | ADDRESS |
+---+---+---+---+---+---+---+---+
Flags | Meaning
--------+---------
0 0 | data broadcast by a node
0 1 | data request by a node
1 0 | negative response from the node (NACK)
1 1 | positive response from the node (ACK)
@endverbatim
*
* When transmitting, the *second bit* determines, whether or not to expect an
* answer from the addressed node.
* To just send information without any need for acknowledgment, use a broadcast.
* When a node is responding, the *second bit* has to following meaning:
*
* - `true` - Message is an positive response and may contain a payload
* - `false` - Message signals an error condition and carries only one byte of
* payload. This byte is an error code.
*
* @section amnb_electric Electrical characteristics
*
* Between different boards CAN transceivers are used. Compared to RS485 the
* CAN transceivers have the advantage to work without a separate direction input.
* You can just connected the transceiver directly to the UART of your
* microcontroller.
* These are identical to the SAB CAN electrical characteristics.
* You have to use the CAN transceivers, otherwise it cannot be determined, if
* the bus is busy or available for transmission.
*
* @author Fabian Greif
* @author Niklas Hauser
*/

#ifndef MODM_AMNB_HPP
#define MODM_AMNB_HPP

#include "amnb/node.hpp"

#endif // MODM_AMNB_HPP
107 changes: 0 additions & 107 deletions src/modm/communication/amnb/constants.hpp

This file was deleted.

Loading