Skip to content

Commit

Permalink
Adds driver and hooks for sending railcom data out. (#551)
Browse files Browse the repository at this point in the history
This PR enables the dcc_decoder code to interact with a RailCom driver in a way that allows sending railcom data back to the track.
- Adds the ability to adjust what timing the railcom driver gets called. This is expectedto change when we generate a cutout versus when we trigger the UART to send data.
- adds calls to the RailcomDriver to specify the feedback to be sent
- Ensures that railcom data only gets sent for the packet that it belongs to
- Adds an STM32 implementation for the railcom send driver, which is derived from the Stm32Uart driver.
- Adds a hook in the dcc_decode interrupt that calls an application provided module to compute what railcom feedback we should be sending.

====

* Adds a delta capability for tuning the railcom cutout timing.

* Fixes up the compilation of the Tiva DCC decoder target.

* Adds API for railcom sender implementation.

* Adds railcom sender implementation for STM32.

* Adds a call in the DCC interrupt to fill in the railcom feedback.

* Adds the send functions to the railcom driver.

* Fixes bugs in the stm32 railcom sender.

* Removes unneeded forward declaration.
  • Loading branch information
balazsracz authored Aug 2, 2021
1 parent 0c9e3a9 commit 05e5d61
Show file tree
Hide file tree
Showing 11 changed files with 398 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,24 @@ struct DccDecoderHW
/// driver to take a feedback sample.
static inline void after_feedback_hook() {}

/// How many usec later/earlier should the railcom cutout start happen.
static int time_delta_railcom_pre_usec()
{
return 0;
}

/// How many usec later/earlier should the railcom cutout middle happen.
static int time_delta_railcom_mid_usec()
{
return 0;
}

/// How many usec later/earlier should the railcom cutout end happen.
static int time_delta_railcom_end_usec()
{
return 0;
}

/// Second timer resource that will be used to measure microseconds for the
/// railcom cutout. May be the same as the Capture Timer, if there are at
/// least two channels on that timer resource.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,23 @@ struct DCCDecode
static inline void dcc_before_cutout_hook() {}
static inline void dcc_packet_finished_hook() {}
static inline void after_feedback_hook() {}
/// How many usec later/earlier should the railcom cutout start happen.
static int time_delta_railcom_pre_usec()
{
return 0;
}

/// How many usec later/earlier should the railcom cutout middle happen.
static int time_delta_railcom_mid_usec()
{
return 0;
}

/// How many usec later/earlier should the railcom cutout end happen.
static int time_delta_railcom_end_usec()
{
return 0;
}
};

// Dummy implementation because we are not a railcom detector.
Expand Down
54 changes: 54 additions & 0 deletions src/dcc/PacketProcessor.hxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/** \copyright
* Copyright (c) 2021, Balazs Racz
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* \file PacketProcessor.hxx
*
* Interface used for processing DCC packets to generate the feedback data.
*
* @author Balazs Racz
* @date 10 July 2021
*/

#include "dcc/packet.h"

class RailcomDriver;

namespace dcc
{

/// Abstract class that is used as a plugin in the DCC decoder. The application
/// logic can implement this class and it will be called from the
/// interrupt. This is necessary to correctly generate the RailCom feedback to
/// be sent back.
class PacketProcessor
{
public:
/// Called in an OS interrupt with the arrived packet.
virtual void packet_arrived(
const DCCPacket *pkt, RailcomDriver *railcom) = 0;
};

} // namespace dcc
33 changes: 30 additions & 3 deletions src/freertos_drivers/common/DccDecoder.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@

#include "RailcomDriver.hxx" // for debug pins
#include "dcc/Receiver.hxx"
#include "dcc/PacketProcessor.hxx"
#include "dcc/packet.h"

/**
Expand Down Expand Up @@ -95,6 +96,14 @@ public:
inputData_->destroy();
}

/// Installs a hook that will be called in the interrupt context for each
/// incoming packet.
/// @param p the hook interface to be called.
void set_packet_processor(dcc::PacketProcessor *p)
{
packetProcessor_ = p;
}

/** Handles a raw interrupt. */
inline void interrupt_handler() __attribute__((always_inline));

Expand Down Expand Up @@ -212,12 +221,17 @@ private:
bool prepCutout_ = false;
/// Which window of the cutout we are in.
uint32_t cutoutState_;
/// Counts unique identifiers for DCC packets to be returned.
uint32_t packetId_ = 0;
/// How many times did we lose a DCC packet due to no buffer available.
//uint32_t overflowCount_ {0};

/// notified for cutout events.
RailcomDriver *railcomDriver_;

/// notified for every arrived DCC / MM packet within the interrupt.
dcc::PacketProcessor* packetProcessor_ = nullptr;

/// DCC packet decoder state machine and internal state.
dcc::DccDecoder decoder_ {Module::get_ticks_per_usec()};

Expand Down Expand Up @@ -302,6 +316,12 @@ __attribute__((optimize("-O3"))) void DccDecoder<Module>::interrupt_handler()
if (decoder_.before_dcc_cutout())
{
prepCutout_ = true;
auto* p = decoder_.pkt();
if (p)
{
p->feedback_key = ++packetId_;
}
railcomDriver_->set_feedback_key(packetId_);
Module::dcc_before_cutout_hook();
}
// If we are at the second half of the last 1 bit and the
Expand All @@ -314,7 +334,8 @@ __attribute__((optimize("-O3"))) void DccDecoder<Module>::interrupt_handler()
{
//Debug::RailcomDriverCutout::set(true);
Module::set_cap_timer_time();
Module::set_cap_timer_delay_usec(RAILCOM_CUTOUT_PRE);
Module::set_cap_timer_delay_usec(
RAILCOM_CUTOUT_PRE + Module::time_delta_railcom_pre_usec());
inCutout_ = true;
cutoutState_ = 0;
if (decoder_.pkt())
Expand Down Expand Up @@ -365,14 +386,16 @@ DccDecoder<Module>::rcom_interrupt_handler()
{
case 0:
{
Module::set_cap_timer_delay_usec(RAILCOM_CUTOUT_MID);
Module::set_cap_timer_delay_usec(
RAILCOM_CUTOUT_MID + Module::time_delta_railcom_mid_usec());
railcomDriver_->start_cutout();
cutoutState_ = 1;
break;
}
case 1:
{
Module::set_cap_timer_delay_usec(RAILCOM_CUTOUT_END);
Module::set_cap_timer_delay_usec(
RAILCOM_CUTOUT_END + Module::time_delta_railcom_end_usec());
railcomDriver_->middle_cutout();
cutoutState_ = 2;
break;
Expand All @@ -396,6 +419,10 @@ __attribute__((optimize("-O3"))) void DccDecoder<Module>::os_interrupt_handler()
unsigned woken = 0;
if (nextPacketFilled_)
{
if (packetProcessor_)
{
packetProcessor_->packet_arrived(decoder_.pkt(), railcomDriver_);
}
inputData_->advance(1);
nextPacketFilled_ = false;
inputData_->signal_condition_from_isr();
Expand Down
25 changes: 25 additions & 0 deletions src/freertos_drivers/common/RailcomDriver.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@
#ifndef _FREERTOS_DRIVERS_COMMON_RAILCOMDRIVER_HXX_
#define _FREERTOS_DRIVERS_COMMON_RAILCOMDRIVER_HXX_

#include <inttypes.h>

#include "utils/macros.h"
#include "dcc/railcom.h"

/// Abstract base class for railcom drivers. This interface is used to
/// communicate when the railcom cutout happens. The railcom cutout is produced
/// or detected in the DCC generator or DCC parser driver, but the railcom
Expand Down Expand Up @@ -67,6 +72,26 @@ public:
* shall be called before start_cutout. The feedback key set here is used
* until this method is called again. @param key is the new feedback key. */
virtual void set_feedback_key(uint32_t key) = 0;

/** Specifies what packet should be sent for the channel1 cutout. It is
* okay to specify the same packet pointer for ch1 and ch2 cutout.
* @param ch1_pkt the RailCom packet. Only the ch1 data will be read from
* this packet. This pointer must stay alive until the next DCC packet
* comes. The FeedbackKey in this packet must be correct for the current
* DCC packet or else the data will not be sent. */
virtual void send_ch1(const DCCFeedback *ch1_pkt)
{
}

/** Specifies what packet should be sent for the channel2 cutout. It is
* okay to specify the same packet pointer for ch1 and ch2 cutout.
* @param ch2_pkt the RailCom packet. Only the ch2 data will be read from
* this packet. This pointer must stay alive until the next DCC packet
* comes. The FeedbackKey in this packet must be correct for the current
* DCC packet or else the data will not be sent. */
virtual void send_ch2(const DCCFeedback *ch2_pkt)
{
}
};


Expand Down
18 changes: 18 additions & 0 deletions src/freertos_drivers/st/Stm32DCCDecoder.hxx
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,24 @@ public:
HW::after_feedback_hook();
}

/// How many usec later should the railcom cutout start happen.
static int time_delta_railcom_pre_usec()
{
return HW::time_delta_railcom_pre_usec();
}

/// How many usec later should the railcom cutout middle happen.
static int time_delta_railcom_mid_usec()
{
return HW::time_delta_railcom_mid_usec();
}

/// How many usec later should the railcom cutout middle happen.
static int time_delta_railcom_end_usec()
{
return HW::time_delta_railcom_end_usec();
}

/// Called from the capture interrupt handler. Checks interrupt status,
/// clears interrupt.
/// @return true if the interrupt was generated by a capture event.
Expand Down
88 changes: 88 additions & 0 deletions src/freertos_drivers/st/Stm32RailcomSender.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/** \copyright
* Copyright (c) 2021, Balazs Racz
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* \file Stm32RailcomSender.hxx
*
* Implements a RailcomDriver that sends outgoing railcom data through a UART
* TX on an STM32 target. Designed to be invoked from the priority zero
* interrupt of the DCC decoder.
*
* @author Balazs Racz
* @date July 10, 2021
*/

#include "freertos_drivers/st/Stm32RailcomSender.hxx"

#include "stm32f_hal_conf.hxx"

/// Called at the beginning of the first window.
void Stm32RailcomSender::start_cutout()
{
const auto* pkt = ch1Pkt_;
if (!pkt || pkt->feedbackKey != expectedFeedbackKey_)
{
// Nothing to send or came too late and should not be sent.
return;
}
if (!__HAL_UART_GET_IT(&uartHandle, UART_IT_TXE)) {
// Transmission is not complete yet. That's weird.
return;
}
if (pkt->ch1Size == 0) {
// Nothing to send.
return;
}
uartHandle.Instance->TDR = pkt->ch1Data[0];
if (pkt->ch1Size > 1)
{
txBuf->put(pkt->ch1Data + 1, 1);
__HAL_UART_ENABLE_IT(&uartHandle, UART_IT_TXE);
}
}

void Stm32RailcomSender::middle_cutout()
{
const auto* pkt = ch2Pkt_;
if (!pkt || pkt->feedbackKey != expectedFeedbackKey_)
{
// Nothing to send or came too late and should not be sent.
return;
}
if (!__HAL_UART_GET_IT(&uartHandle, UART_IT_TXE)) {
// Transmission is not complete yet. That's weird.
return;
}
if (pkt->ch2Size == 0) {
// Nothing to send.
return;
}
uartHandle.Instance->TDR = pkt->ch2Data[0];
if (pkt->ch2Size > 1)
{
txBuf->put(pkt->ch2Data + 1, pkt->ch2Size - 1);
__HAL_UART_ENABLE_IT(&uartHandle, UART_IT_TXE);
}
}
Loading

0 comments on commit 05e5d61

Please sign in to comment.