You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
307 lines
9.0 KiB
307 lines
9.0 KiB
/* |
|
|
|
(c) 2017 night_ghost@ykoctpa.ru |
|
|
|
based on: ST appnote |
|
|
|
* SerialDriver.cpp --- AP_HAL_F4Light SoftSerial driver. |
|
* |
|
*/ |
|
|
|
#pragma GCC optimize ("O2") |
|
|
|
#include <AP_HAL/AP_HAL.h> |
|
|
|
#if CONFIG_HAL_BOARD == HAL_BOARD_F4LIGHT && defined(BOARD_SOFTSERIAL_RX) && defined(BOARD_SOFTSERIAL_TX) |
|
#include "UART_SoftDriver.h" |
|
|
|
#include <stdio.h> |
|
#include <unistd.h> |
|
#include <stdlib.h> |
|
#include <errno.h> |
|
#include <fcntl.h> |
|
|
|
#include <gpio_hal.h> |
|
|
|
|
|
using namespace F4Light; |
|
|
|
|
|
// hardware RX and software TX |
|
|
|
void SerialDriver::begin(uint32_t baud) { |
|
gpio_write_bit(tx_pp.gpio_device, tx_pp.gpio_bit, _inverse?LOW:HIGH); |
|
gpio_set_mode(tx_pp.gpio_device, tx_pp.gpio_bit, GPIO_OUTPUT_PP); |
|
gpio_set_mode(rx_pp.gpio_device, rx_pp.gpio_bit, GPIO_INPUT_PU); |
|
|
|
|
|
timer_pause(timer); |
|
uint32_t prescaler; |
|
|
|
if (baud > 2400) { |
|
bitPeriod = (uint16_t)((uint32_t)(CYCLES_PER_MICROSECOND * 1000000) / baud); |
|
prescaler=1; |
|
} else { |
|
bitPeriod = (uint16_t)(((uint32_t)(CYCLES_PER_MICROSECOND * 1000000) / 16) / baud); |
|
prescaler=16; |
|
} |
|
|
|
timer_set_prescaler(timer, prescaler-1); |
|
|
|
timer_set_reload(timer, bitPeriod/2); // for TX needs |
|
|
|
transmitBufferRead = transmitBufferWrite = 0; |
|
txBitCount = 8; // 1st interrupt will generate STOP |
|
txSkip=true; |
|
|
|
// Set rx State machine start state, attach the bit interrupt and mask it until start bit is received |
|
receiveBufferRead = receiveBufferWrite = 0; |
|
rxBitCount = 9; |
|
|
|
rxSetCapture(); // wait for start bit |
|
timer_attach_interrupt(timer, channel, Scheduler::get_handler(FUNCTOR_BIND_MEMBER(&SerialDriver::rxNextBit,void)), SOFT_UART_INT_PRIORITY); |
|
timer_attach_interrupt(timer, TIMER_UPDATE_INTERRUPT, Scheduler::get_handler(FUNCTOR_BIND_MEMBER(&SerialDriver::txNextBit,void)), SOFT_UART_INT_PRIORITY); // also enables interrupt, so 1st interrupt will be ASAP |
|
// there will be interrupt after enabling TX interrupts, it will be disabled in handler |
|
|
|
timer_generate_update(timer); // Load the timer values and start it |
|
timer_resume(timer); |
|
|
|
_initialized = true; |
|
} |
|
|
|
|
|
void SerialDriver::rxSetCapture(){ |
|
timer_ic_set_mode(timer, channel, TIM_ICSelection_DirectTI | TIM_ICPSC_DIV1, 3); |
|
timer_cc_set_pol(timer, channel, _inverse?TIMER_POLARITY_RISING:TIMER_POLARITY_FALLING); |
|
} |
|
|
|
void SerialDriver::rxSetCompare(){ |
|
timer_set_mode(timer, channel, TIMER_OUTPUT_COMPARE); // for RX needs, capture mode by hands |
|
} |
|
|
|
|
|
void SerialDriver::end() { |
|
timer_pause(timer); |
|
gpio_write_bit(tx_pp.gpio_device, tx_pp.gpio_bit, 1); |
|
_initialized = false; |
|
|
|
} |
|
|
|
void SerialDriver::flush() { |
|
receiveBufferRead = receiveBufferWrite = 0; |
|
} |
|
|
|
|
|
bool SerialDriver::tx_pending() { |
|
if(!_initialized) return 0; |
|
|
|
return (transmitBufferWrite + SS_MAX_TX_BUFF - transmitBufferRead) % SS_MAX_TX_BUFF; |
|
} |
|
|
|
|
|
uint32_t SerialDriver::available() { |
|
|
|
return (receiveBufferWrite + SS_MAX_RX_BUFF - receiveBufferRead) % SS_MAX_RX_BUFF; |
|
} |
|
|
|
uint32_t SerialDriver::txspace() { |
|
return SS_MAX_TX_BUFF - tx_pending(); |
|
} |
|
|
|
int16_t SerialDriver::read() { |
|
if (!_initialized) |
|
return -1; |
|
|
|
// Wait if buffer is empty |
|
if(receiveBufferRead == receiveBufferWrite) return -1; // no data |
|
|
|
uint8_t inData = receiveBuffer[receiveBufferRead]; |
|
|
|
receiveBufferRead = (receiveBufferRead + 1) % SS_MAX_RX_BUFF; |
|
|
|
return inData; |
|
} |
|
|
|
size_t SerialDriver::write(uint8_t c) { |
|
if (!_initialized) return 0; |
|
|
|
// Blocks if buffer full |
|
uint16_t n_try=3; |
|
do { // wait for free space |
|
if( ((transmitBufferWrite + 1) % SS_MAX_TX_BUFF) == transmitBufferRead ){ |
|
Scheduler::yield(); // while we wait - let others work |
|
if(! _blocking) n_try--; |
|
} else break; // got it! |
|
} while(n_try); |
|
|
|
// Save new data in buffer and bump the write pointer |
|
transmitBuffer[transmitBufferWrite] = c; |
|
|
|
transmitBufferWrite = (transmitBufferWrite == SS_MAX_TX_BUFF) ? 0 : transmitBufferWrite + 1; |
|
|
|
|
|
// Check if transmit timer interrupt enabled and if not unmask it |
|
// transmit timer interrupt will get masked by transmit ISR when buffer becomes empty |
|
if (!activeTX) { |
|
activeTX=true; |
|
|
|
// Set state to 10 (send start bit) and re-enable transmit interrupt |
|
txBitCount = 10; |
|
|
|
txEnableInterrupts(); // enable |
|
} |
|
|
|
return 1; |
|
} |
|
|
|
size_t SerialDriver::write(const uint8_t *buffer, size_t size) |
|
{ |
|
size_t n = 0; |
|
while (size--) { |
|
n += write(*buffer++); |
|
} |
|
return n; |
|
} |
|
|
|
|
|
#define bitRead(value, bit) (((value) >> (bit)) & 0x01) |
|
|
|
// Transmits next bit. Called by timer update interrupt |
|
void SerialDriver::txNextBit(void /* TIM_TypeDef *tim */) { // ISR |
|
|
|
txSkip= !txSkip; |
|
|
|
if(txSkip) return; // one bit per 2 periods |
|
|
|
|
|
// State 0 through 7 - transmit bits |
|
if (txBitCount <= 7) { |
|
if (bitRead(transmitBuffer[transmitBufferRead], txBitCount) == (_inverse?0:1)) { |
|
gpio_write_bit(tx_pp.gpio_device, tx_pp.gpio_bit,HIGH); |
|
} else { |
|
gpio_write_bit(tx_pp.gpio_device, tx_pp.gpio_bit,LOW); |
|
} |
|
|
|
// Bump the bit/state counter to state 8 |
|
txBitCount++; |
|
|
|
#if DEBUG_DELAY && defined(DEBUG_PIN1) |
|
GPIO::_write(DEBUG_PIN1,1); |
|
GPIO::_write(DEBUG_PIN1,0); |
|
#endif |
|
|
|
// State 8 - Send the stop bit and reset state to state -1 |
|
// Shutdown timer interrupt if buffer empty |
|
} else if (txBitCount == 8) { |
|
|
|
// Send the stop bit |
|
gpio_write_bit(tx_pp.gpio_device, tx_pp.gpio_bit, _inverse?LOW:HIGH); |
|
|
|
transmitBufferRead = (transmitBufferRead == SS_MAX_TX_BUFF ) ? 0 : transmitBufferRead + 1; |
|
|
|
if (transmitBufferRead != transmitBufferWrite) { // we have data do transmit |
|
txBitCount = 10; |
|
} else { |
|
// Buffer empty so shutdown timer until write() puts data in |
|
txDisableInterrupts(); |
|
activeTX=false; |
|
} |
|
|
|
// Send start bit for new byte |
|
} else if (txBitCount >= 10) { |
|
gpio_write_bit(tx_pp.gpio_device, tx_pp.gpio_bit, _inverse?HIGH:LOW); |
|
|
|
txBitCount = 0; |
|
} |
|
} |
|
|
|
|
|
|
|
// Receive next bit. Called by timer channel interrupt |
|
void SerialDriver::rxNextBit(void /* TIM_TypeDef *tim */) { // ISR |
|
|
|
if(!activeRX) { // capture start bit |
|
|
|
// Test if this is really the start bit and not a spurious edge |
|
if (rxBitCount == 9) { |
|
|
|
uint16_t pos = timer_get_capture(timer, channel); |
|
|
|
rxSetCompare(); // turn to compare mode |
|
|
|
timer_set_compare(timer, channel, pos); // captured value |
|
|
|
// Set state/bit to first bit |
|
rxSkip=false; // next half bit will OK |
|
activeRX=true; |
|
} |
|
} else { // compare match twice per bit; |
|
rxSkip= !rxSkip; |
|
|
|
if(!rxSkip) return; // not the middle of bit |
|
|
|
// now in middle of bit |
|
uint8_t d = gpio_read_bit( rx_pp.gpio_device, rx_pp.gpio_bit); |
|
|
|
if (rxBitCount == 9) { // check start bit again |
|
if ( d == _inverse?HIGH:LOW) { // start OK |
|
rxBitCount = 0; |
|
} else { // false start |
|
activeRX=false; |
|
rxSetCapture(); // turn back to capture mode |
|
} |
|
} else if (rxBitCount < 8) { // get bits |
|
receiveByte >>= 1; |
|
|
|
|
|
if ( d == _inverse?LOW:HIGH) |
|
receiveByte |= 0x80; |
|
|
|
#if DEBUG_DELAY |
|
GPIO::_write(DEBUG_PIN,1); |
|
GPIO::_write(DEBUG_PIN,0); |
|
#endif |
|
|
|
|
|
rxBitCount++; |
|
|
|
// State 8 - Save incoming byte and update buffer |
|
} else if (rxBitCount == 8) { |
|
if ( d == _inverse?LOW:HIGH) { // stop OK - save byte |
|
// Finish out stop bit while we... |
|
|
|
if (gpio_read_bit( rx_pp.gpio_device, rx_pp.gpio_bit) == _inverse?LOW:HIGH) // valid STOP |
|
receiveBuffer[receiveBufferWrite] = receiveByte; |
|
|
|
// Calculate location in buffer for next incoming byte |
|
uint8_t next = (receiveBufferWrite + 1) % SS_MAX_RX_BUFF; // FYI - With this logic we effectively only have an (SS_MAX_RX_BUFF - 1) buffer size |
|
|
|
// Test if buffer full |
|
// If the buffer isn't full update the tail pointer to point to next location |
|
if (next != receiveBufferRead) { |
|
receiveBufferWrite = next; |
|
} |
|
#ifdef SS_DEBUG |
|
else { |
|
bufferOverflow = true; // Else if it is now full set the buffer overflow flag |
|
|
|
|
|
#if DEBUG_DELAY && defined(DEBUG_PIN1) |
|
overFlowTail = receiveBufferWrite; |
|
overFlowHead = receiveBufferRead; |
|
|
|
GPIO::_write(DEBUG_PIN1, 1); |
|
GPIO::_write(DEBUG_PIN1, 0); |
|
#endif |
|
} |
|
#endif |
|
} |
|
// Set for state 9 to receive next byte |
|
rxBitCount = 9; |
|
activeRX=false; |
|
rxSetCapture(); // turn back to capture mode |
|
} |
|
} |
|
} |
|
|
|
#endif
|
|
|