ardupilot/libraries/AP_HAL_F4Light/UART_SoftDriver.h

172 lines
6.8 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// inspired by AN4655
// http://www.st.com/content/ccc/resource/technical/document/application_note/3d/80/91/f9/4c/25/4a/26/DM00160482.pdf/files/DM00160482.pdf/jcr:content/translations/en.DM00160482.pdf
// last in page http://www.st.com/content/st_com/en/products/microcontrollers/stm32-32-bit-arm-cortex-mcus/stm32l0-series/stm32l0x2/stm32l052c6.html
/*
This example shows how to control both transmit and receive processes by software, based on HW events.
Overflow and IO capture events of a single Capture & Compare timer is used to control timing of emulation of UART input sampling and
output handling of the Rx and Tx signals. The associated input capture pin is dedicated to receive data. Any general-purpose output
pin or associated output compare pin can be used for data transmission. The timer overflow period is set to the channel half bit rate
by setting the timer auto reload register.
illustrates the timing diagram.
Figure 3. UART duplex channel based on timer capture events
Transmission process control is based on regular interrupts from the timer overflow events.
After data for transmission is stored into dedicated variable (transmit buffer simulated by SW), an internal transmission request appears and
the nearest overflow interrupt is used to start a new transmission (maximum latency for transmission start could be half a bit period).
This is because the same timer is used for control both transmit and receive processes not synchronized normally. Every even timer overflow
event interrupt controls the consecutive edge changes of the transmitted signal on the dedicated output pin until the end of the frame
transmission. Odd overflow interrupts are discarded.
The receive process uses both input capture and output compare feature of the same timer and its dedicated pin.
Initially, the input capture is performed at the pin. After detecting the first falling edge, the value captured in the
capture register is then used for compare purposes because the input pin functionality is switched to output compare mode (without
affecting any output GPIO capability since the pin still stays in input mode). Due to the half-a-bit overflow period of the timer, the nearest
output compare event points to the middle of the first receive bit (start bit). Sampling can be simulated by three consecutive reading of the
input pin level at that moment and if, for each of them, a low level is detected, the correctly received start bit is evaluated. The receive
process then continues by watching every next odd output compare event. The same three point sampling method can be performed with noise
detection logic here and for all the other received data bits until the end of the current receive frame. All even compare interrupts are
discarded. After the stop bits of the frame are sampled, the Rx pin is switched back to input capture mode and waits for the next frame
start condition. The detection of noise while the stop bits are being sampled should cause a frame error. If a noise is detected during start
bit, the receive process should be aborted and the Rx pin switched back to input capture mode while waiting for the next falling edge capture.
User can build an API interface upon this low level HW abstraction level. It can include the UART channel initialization, enable and disabl
e Rx and Tx channels, read and write the data flow (e.g. control the data buffers) and check the transactions status. Size of data, parity
control number of stop bits or other control can be solved on pre-compilation level by conditional compilation to speed up the code when
these features are not used.
(C)
in case of RevoMini we have pins
14, // PC8 T8/3 - Soft_scl or soft_tx
15, // PC9 T8/4 - Soft_sda or soft_rx
*/
#pragma once
#if defined(BOARD_SOFTSERIAL_RX) && defined(BOARD_SOFTSERIAL_TX)
#include <AP_HAL_F4Light/AP_HAL_F4Light.h>
#include <usart.h>
#include <gpio_hal.h>
#include <pwm_in.h>
#include "Scheduler.h"
#include "GPIO.h"
#define DEFAULT_TX_TIMEOUT 10000
#define SS_DEBUG
#define SSI_RX_BUFF_SIZE 64
#define SSI_TX_BUFF_SIZE 64
#define SS_MAX_RX_BUFF (SSI_RX_BUFF_SIZE - 1) // RX buffer size
#define SS_MAX_TX_BUFF (SSI_TX_BUFF_SIZE - 1) // TX buffer size
extern const struct TIM_Channel PWM_Channels[];
class F4Light::SerialDriver : public AP_HAL::UARTDriver {
public:
SerialDriver( uint8_t tx_pin, uint8_t rx_pin, bool inverseLogic)
:_inverse(inverseLogic)
, _initialized(false)
, _blocking(true)
, txSkip(false)
, rxSkip(false)
, activeRX(false)
, activeTX(false)
,_tx_pin(tx_pin)
,_rx_pin(rx_pin)
, tx_pp(PIN_MAP[tx_pin])
, rx_pp(PIN_MAP[rx_pin])
, timer(rx_pp.timer_device)
, channel(rx_pp.timer_channel)
{
}
/* F4Light implementations of UARTDriver virtual methods */
void begin(uint32_t b);
void begin(uint32_t baud, uint16_t rxS, uint16_t txS) { begin(baud); }
void end();
void flush();
bool inline is_initialized(){ return _initialized; }
void inline set_blocking_writes(bool blocking) { _blocking=blocking; }
bool tx_pending();
/* F4Light implementations of Stream virtual methods */
uint32_t available() override;
uint32_t txspace() override;
int16_t read() override;
/* Empty implementations of Print virtual methods */
size_t write(uint8_t c);
size_t write(const uint8_t *buffer, size_t size);
private:
bool _inverse;
bool _initialized;
bool _blocking;
uint16_t bitPeriod;
#ifdef SS_DEBUG
volatile uint8_t bufferOverflow;
#endif
volatile int8_t rxBitCount;
volatile uint16_t receiveBufferWrite;
volatile uint16_t receiveBufferRead;
volatile uint8_t receiveBuffer[SSI_RX_BUFF_SIZE];
uint8_t receiveByte;
volatile int8_t txBitCount;
uint16_t transmitBufferWrite;
volatile uint16_t transmitBufferRead;
uint8_t transmitBuffer[SSI_RX_BUFF_SIZE];
bool txSkip;
bool rxSkip;
bool activeRX;
bool activeTX;
uint8_t _tx_pin;
uint8_t _rx_pin;
const stm32_pin_info &tx_pp;
const stm32_pin_info &rx_pp;
const timer_dev *timer;
const timer_Channel channel;
// Clear pending interrupt and enable receive interrupt
// Note: Clears pending interrupt
inline void txEnableInterrupts() {
timer->regs->SR &= ~TIMER_SR_UIF;
timer_enable_irq(timer, TIMER_UPDATE_INTERRUPT);
}
// Mask transmit interrupt
inline void txDisableInterrupts() {
timer_disable_irq(timer, TIMER_UPDATE_INTERRUPT);
}
void rxSetCapture();
void rxSetCompare();
void txNextBit( /* TIM_TypeDef *tim */ );
void rxNextBit( /* TIM_TypeDef *tim */ );
};
#endif // defined(BOARD_SOFTSERIAL_RX) && defined(BOARD_SOFTSERIAL_TX)