Обновление до версии 0.3.0
- обновлен elbear_fw_bootloader - добавлена проверка контрольной суммы каждой строки hex файла. - в модуль работы с АЦП добавлена функция analogReadResolution(). Функция analogRead() теперь возвращает усредненное по 10 измерениям значение. - общая функция обработки прерываний перенесена в память RAM. Обработчики прерываний модулей External Interrupts и Advanced I/O (функция tone()) так же перенесены в память RAM для увеличения скорости выполнения кода. - в пакет добавлены библиотеки EEPROM, Servo, SoftSerial, NeoPixel, MFRC522 адаптированные для работы с платой Elbear Ace-Uno. - добавлено описание особенностей работы с пакетом
This commit was merged in pull request #6.
This commit is contained in:
1 parent
7a7c552743
commit
766b7b32ea
114 files changed
+11064
-666
No files matched your search
@@ -0,0 +1,361 @@
|
||||
/*
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
The latest version of this library can always be found at
|
||||
http://arduiniana.org.
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <SoftwareSerial.h>
|
||||
#include "wiring_LL.h"
|
||||
|
||||
#define MAX_SOFT_SERIAL_SPEED 115200
|
||||
#define MIN_SOFT_SERIAL_SPEED 300
|
||||
|
||||
// Statics
|
||||
// static variable is common to all class instances
|
||||
SoftwareSerial *SoftwareSerial::active_object = 0;
|
||||
uint8_t SoftwareSerial::_receive_buffer[_SS_MAX_RX_BUFF];
|
||||
volatile uint8_t SoftwareSerial::_receive_buffer_tail = 0;
|
||||
volatile uint8_t SoftwareSerial::_receive_buffer_head = 0;
|
||||
|
||||
// Private methods
|
||||
|
||||
// function placed in ram. Should not call other function placed in flash
|
||||
void SoftwareSerial::tunedDelay(uint32_t delayTicks) // .ram_text
|
||||
{
|
||||
if (delayTicks > 1)
|
||||
{
|
||||
uint64_t startTicks = SYSTICK_GET_TICKS();
|
||||
while ((SYSTICK_GET_TICKS() - startTicks) < delayTicks)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
// This function sets the current object as the "listening"
|
||||
// one and returns true if it replaces another
|
||||
bool SoftwareSerial::listen()
|
||||
{
|
||||
if (!_rx_delay_stopbit)
|
||||
return false;
|
||||
|
||||
if (active_object != this)
|
||||
{
|
||||
if (active_object)
|
||||
active_object->stopListening();
|
||||
|
||||
_buffer_overflow = false;
|
||||
_receive_buffer_head = _receive_buffer_tail = 0;
|
||||
active_object = this;
|
||||
|
||||
setRxIntMsk(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Stop listening. Returns true if we were actually listening.
|
||||
bool SoftwareSerial::stopListening()
|
||||
{
|
||||
if (active_object == this)
|
||||
{
|
||||
setRxIntMsk(false);
|
||||
active_object = NULL;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// The receive routine called by the interrupt handler
|
||||
//
|
||||
void SoftwareSerial::recv() // .ram_text
|
||||
{
|
||||
// If RX line is high, then we don't see any start bit
|
||||
// so interrupt is probably not for us
|
||||
bool rxState = (bool)GPIO_READ_PIN((GPIO_TypeDef *)_receivePortRegister, (HAL_PinsTypeDef)_receiveBitMask);
|
||||
if (_inverse_logic ? rxState : !rxState)
|
||||
{
|
||||
// Disable further interrupts during reception, this prevents triggering another interrupt
|
||||
// directly after we return, which can cause problems at higher baudrates.
|
||||
setRxIntMsk(false); // __always_inline__
|
||||
|
||||
// Wait approximately 1/2 of a bit width to "center" the sample
|
||||
if (_rx_delay_centering > 0)
|
||||
tunedDelay(_rx_delay_centering); // .ram_text
|
||||
|
||||
// Read each of the 8 bits
|
||||
uint8_t data = 0;
|
||||
for (uint8_t i=8; i > 0; --i)
|
||||
{
|
||||
tunedDelay(_rx_delay_intrabit);
|
||||
data >>= 1;
|
||||
if (GPIO_READ_PIN((GPIO_TypeDef *)_receivePortRegister, (HAL_PinsTypeDef)_receiveBitMask))
|
||||
data |= 0x80;
|
||||
}
|
||||
if (_inverse_logic)
|
||||
data = ~data;
|
||||
|
||||
// if buffer full, set the overflow flag and return
|
||||
uint8_t next = (_receive_buffer_tail + 1) % _SS_MAX_RX_BUFF;
|
||||
if (next != _receive_buffer_head)
|
||||
{
|
||||
// save new data in buffer: tail points to where byte goes
|
||||
_receive_buffer[_receive_buffer_tail] = data; // save new byte
|
||||
_receive_buffer_tail = next;
|
||||
}
|
||||
else
|
||||
_buffer_overflow = true;
|
||||
|
||||
// skip the stop bit
|
||||
tunedDelay(_rx_delay_stopbit);
|
||||
|
||||
// Re-enable interrupts when we're sure to be inside the stop bit
|
||||
setRxIntMsk(true);
|
||||
}
|
||||
GPIO_IRQ_CLEAR_ALL();
|
||||
}
|
||||
|
||||
uint8_t SoftwareSerial::rx_pin_read()
|
||||
{
|
||||
return (uint8_t)GPIO_READ_PIN((GPIO_TypeDef *)_receivePortRegister, (HAL_PinsTypeDef)_receiveBitMask);
|
||||
}
|
||||
|
||||
// Interrupt handling
|
||||
|
||||
/* static */
|
||||
void SoftwareSerial::handle_interrupt() // .ram_text
|
||||
{
|
||||
if (active_object)
|
||||
{
|
||||
active_object->recv(); // .ram_text
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void __attribute__((noinline, section(".ram_text"))) softSerial_interrupt_handler(void)
|
||||
{
|
||||
SoftwareSerial::handle_interrupt();
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Constructor
|
||||
//
|
||||
SoftwareSerial::SoftwareSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic /* = false */) :
|
||||
_rx_delay_centering(0),
|
||||
_rx_delay_intrabit(0),
|
||||
_rx_delay_stopbit(0),
|
||||
_tx_delay(0),
|
||||
_buffer_overflow(false),
|
||||
_inverse_logic(inverse_logic)
|
||||
{
|
||||
_transmitPin = transmitPin;
|
||||
_receivePin = receivePin;
|
||||
}
|
||||
|
||||
//
|
||||
// Destructor
|
||||
//
|
||||
SoftwareSerial::~SoftwareSerial()
|
||||
{
|
||||
end();
|
||||
}
|
||||
|
||||
void SoftwareSerial::setTX(uint8_t tx)
|
||||
{
|
||||
// save pin and port info
|
||||
_transmitBitMask = (uint16_t)digitalPinToBitMask(tx);
|
||||
_transmitPortRegister = (uint32_t)digitalPinToPort(tx);
|
||||
// set state before tx pin initialization to prevent false start bit
|
||||
if (_inverse_logic)
|
||||
GPIO_CLEAR_PIN((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)_transmitBitMask);
|
||||
else
|
||||
GPIO_SET_PIN((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)_transmitBitMask);
|
||||
// init pin as output
|
||||
pinMode(tx, OUTPUT);
|
||||
}
|
||||
|
||||
void SoftwareSerial::setRX(uint8_t rx)
|
||||
{
|
||||
// save pin, port and gpio_line info
|
||||
_receiveBitMask = (uint16_t)digitalPinToBitMask(rx);
|
||||
_receivePortRegister = (uint32_t)digitalPinToPort(rx);
|
||||
_int_maskLine = digitalPinToGpioIntLine(_receivePin);
|
||||
|
||||
// attach interrupt to rx pin if available
|
||||
attachInterrupt(digitalPinToInterrupt(_receivePin), softSerial_interrupt_handler, _inverse_logic ? RISING : FALLING);
|
||||
// turn on pull up for rx if logic is not inverse
|
||||
if (!_inverse_logic)
|
||||
{
|
||||
uint8_t pinNumber = PIN_MASK_TO_PIN_NUMBER(_receiveBitMask);
|
||||
if (((GPIO_TypeDef*)_receivePortRegister) == GPIO_0)
|
||||
PIN_SET_PAD_CONFIG(PORT_0_PUPD, pinNumber, HAL_GPIO_PULL_UP);
|
||||
else if (((GPIO_TypeDef*)_receivePortRegister) == GPIO_1)
|
||||
PIN_SET_PAD_CONFIG(PORT_1_PUPD, pinNumber, HAL_GPIO_PULL_UP);
|
||||
}
|
||||
// turn off int line for while (it turning on in attachInterrupt())
|
||||
GPIO_IRQ_LINE_DISABLE(_int_maskLine);
|
||||
}
|
||||
|
||||
uint32_t SoftwareSerial::subtract_cap(uint32_t num, uint16_t sub)
|
||||
{
|
||||
if (num > sub)
|
||||
return num - sub;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
//
|
||||
// Public methods
|
||||
//
|
||||
void SoftwareSerial::begin(long speed)
|
||||
{
|
||||
// delays are empirical values here
|
||||
_rx_delay_centering = _rx_delay_intrabit = _rx_delay_stopbit = _tx_delay = 0;
|
||||
|
||||
// limit speed
|
||||
if (speed > MAX_SOFT_SERIAL_SPEED) speed = MAX_SOFT_SERIAL_SPEED;
|
||||
if (speed < MIN_SOFT_SERIAL_SPEED) speed = MIN_SOFT_SERIAL_SPEED;
|
||||
|
||||
// Precalculate the various delays in number of ticks
|
||||
uint32_t bit_delay = F_CPU / speed;
|
||||
|
||||
// init tx
|
||||
setTX(_transmitPin);
|
||||
_tx_delay = subtract_cap(bit_delay, 35); // 1 bit delay while transmitting data
|
||||
|
||||
// init rx only when we have a valid INT for this pin
|
||||
if (digitalPinToInterrupt(_receivePin) != NOT_AN_INTERRUPT)
|
||||
{
|
||||
// set pin config
|
||||
setRX(_receivePin);
|
||||
|
||||
// We want to have a total delay of 1.5 bit time from start bit. Inside the loop, we already
|
||||
// wait for 1 bit time, so here we wait for 0.5 bit time
|
||||
_rx_delay_centering = subtract_cap(bit_delay / 2, 315);
|
||||
|
||||
// 1 bit time
|
||||
_rx_delay_intrabit = subtract_cap(bit_delay, 60);
|
||||
|
||||
// This delay aims at 3/4 of a bit time, meaning the end of the delay will be at 1/4th of the stopbit.
|
||||
// This allows some extra time for ISR cleanup, which makes 115200 baud at 16Mhz work more reliably
|
||||
_rx_delay_stopbit = subtract_cap(bit_delay * 3 / 4, 142);
|
||||
}
|
||||
else
|
||||
ErrorMsgHandler("SoftwareSerial.begin(): Rx pin does not support interrupts, use different pin");
|
||||
|
||||
tunedDelay(_tx_delay); // if we were low this establishes the end
|
||||
|
||||
listen();
|
||||
}
|
||||
|
||||
void SoftwareSerial::setRxIntMsk(bool enable)
|
||||
{
|
||||
if (enable)
|
||||
GPIO_IRQ_LINE_ENABLE(_int_maskLine);
|
||||
else
|
||||
GPIO_IRQ_LINE_DISABLE(_int_maskLine);
|
||||
}
|
||||
|
||||
void SoftwareSerial::end()
|
||||
{
|
||||
stopListening();
|
||||
}
|
||||
|
||||
// function placed in ram. Should not call other function placed in flash
|
||||
size_t SoftwareSerial::write(uint8_t byte)
|
||||
{
|
||||
if (_tx_delay == 0)
|
||||
return 0;
|
||||
|
||||
if (_inverse_logic)
|
||||
byte = ~byte;
|
||||
|
||||
GLOBAL_IRQ_DISABLE(); // turn off interrupts for a clean txmit
|
||||
|
||||
// Write the start bit
|
||||
if (_inverse_logic)
|
||||
GPIO_SET_PIN((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)_transmitBitMask);
|
||||
else
|
||||
GPIO_CLEAR_PIN((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)_transmitBitMask);
|
||||
tunedDelay(_tx_delay); // wait start bit
|
||||
|
||||
// Write each of the 8 bits in LSB mode
|
||||
for (uint8_t i = 8; i > 0; --i)
|
||||
{
|
||||
if (byte & 1) // choose bit
|
||||
GPIO_SET_PIN((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)_transmitBitMask);
|
||||
else
|
||||
GPIO_CLEAR_PIN((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)_transmitBitMask);
|
||||
tunedDelay(_tx_delay);
|
||||
byte >>= 1;
|
||||
}
|
||||
|
||||
// restore pin to natural state - stop bit
|
||||
if (_inverse_logic)
|
||||
GPIO_CLEAR_PIN((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)_transmitBitMask);
|
||||
else
|
||||
GPIO_SET_PIN((GPIO_TypeDef *)_transmitPortRegister, (HAL_PinsTypeDef)_transmitBitMask);
|
||||
|
||||
// enable interrupts
|
||||
GLOBAL_IRQ_ENABLE();
|
||||
|
||||
tunedDelay(_tx_delay); // wait stop bit
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Read data from buffer
|
||||
int SoftwareSerial::read()
|
||||
{
|
||||
if (!isListening())
|
||||
return -1;
|
||||
|
||||
// Empty buffer?
|
||||
if (_receive_buffer_head == _receive_buffer_tail)
|
||||
return -1;
|
||||
|
||||
// Read from "head"
|
||||
uint8_t data = _receive_buffer[_receive_buffer_head]; // grab next byte
|
||||
_receive_buffer_head = (_receive_buffer_head + 1) % _SS_MAX_RX_BUFF;
|
||||
return data;
|
||||
}
|
||||
|
||||
int SoftwareSerial::available()
|
||||
{
|
||||
if (!isListening())
|
||||
return 0;
|
||||
|
||||
return ((unsigned int)(_receive_buffer_tail + _SS_MAX_RX_BUFF - _receive_buffer_head)) % _SS_MAX_RX_BUFF;
|
||||
}
|
||||
|
||||
void SoftwareSerial::flush()
|
||||
{
|
||||
// There is no tx buffering, simply return
|
||||
}
|
||||
|
||||
int SoftwareSerial::peek()
|
||||
{
|
||||
if (!isListening())
|
||||
return -1;
|
||||
|
||||
// Empty buffer?
|
||||
if (_receive_buffer_head == _receive_buffer_tail)
|
||||
return -1;
|
||||
|
||||
// Read from "head"
|
||||
return _receive_buffer[_receive_buffer_head];
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
SoftwareSerial.h
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
The latest version of this library can always be found at
|
||||
http://arduiniana.org.
|
||||
*/
|
||||
|
||||
#ifndef SoftwareSerial_h
|
||||
#define SoftwareSerial_h
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <Stream.h>
|
||||
|
||||
/******************************************************************************
|
||||
* Definitions
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef _SS_MAX_RX_BUFF
|
||||
#define _SS_MAX_RX_BUFF 64 // RX buffer size
|
||||
#endif
|
||||
|
||||
#ifndef GCC_VERSION
|
||||
#define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
|
||||
#endif
|
||||
|
||||
class SoftwareSerial : public Stream
|
||||
{
|
||||
private:
|
||||
// per object data
|
||||
uint8_t _receivePin;
|
||||
uint16_t _receiveBitMask;
|
||||
uint32_t _receivePortRegister;
|
||||
uint8_t _transmitPin;
|
||||
uint16_t _transmitBitMask;
|
||||
uint32_t _transmitPortRegister;
|
||||
uint8_t _int_maskLine;
|
||||
|
||||
// must never be 0!
|
||||
uint32_t _rx_delay_centering;
|
||||
uint32_t _rx_delay_intrabit;
|
||||
uint32_t _rx_delay_stopbit;
|
||||
uint32_t _tx_delay;
|
||||
|
||||
uint16_t _buffer_overflow:1;
|
||||
uint16_t _inverse_logic:1;
|
||||
|
||||
// static data
|
||||
static uint8_t _receive_buffer[_SS_MAX_RX_BUFF];
|
||||
static volatile uint8_t _receive_buffer_tail;
|
||||
static volatile uint8_t _receive_buffer_head;
|
||||
static SoftwareSerial *active_object;
|
||||
|
||||
// private methods
|
||||
void recv() __attribute__((noinline, section(".ram_text")));
|
||||
uint8_t rx_pin_read();
|
||||
void setTX(uint8_t transmitPin);
|
||||
void setRX(uint8_t receivePin);
|
||||
inline void setRxIntMsk(bool enable) __attribute__((__always_inline__));
|
||||
|
||||
// Return num - sub, or 1 if the result would be < 1
|
||||
static uint32_t subtract_cap(uint32_t num, uint16_t sub);
|
||||
|
||||
// private static method for timing
|
||||
static void tunedDelay(uint32_t delayTicks) __attribute__((noinline, section(".ram_text")));
|
||||
|
||||
public:
|
||||
// public methods
|
||||
SoftwareSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic = false);
|
||||
~SoftwareSerial();
|
||||
void begin(long speed);
|
||||
bool listen();
|
||||
void end();
|
||||
bool isListening() { return this == active_object; }
|
||||
bool stopListening();
|
||||
bool overflow()
|
||||
{
|
||||
bool ret = _buffer_overflow;
|
||||
if (ret)
|
||||
_buffer_overflow = false;
|
||||
return ret;
|
||||
}
|
||||
int peek();
|
||||
|
||||
virtual size_t write(uint8_t byte) __attribute__((noinline, section(".ram_text")));
|
||||
virtual int read();
|
||||
virtual int available();
|
||||
virtual void flush();
|
||||
operator bool() { return true; }
|
||||
|
||||
using Print::write;
|
||||
|
||||
// public only for easy access by interrupt handlers
|
||||
static void handle_interrupt() __attribute__((noinline, section(".ram_text")));
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in new issue
Block a user