mirror of https://github.com/ArduPilot/ardupilot
172 lines
6.8 KiB
C++
172 lines
6.8 KiB
C++
// 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)
|