Skip to content

Instantly share code, notes, and snippets.

@pan-
Created January 11, 2019 17:43
Show Gist options
  • Save pan-/9c836b51116aa8e9c3c262dca60c17b5 to your computer and use it in GitHub Desktop.
Save pan-/9c836b51116aa8e9c3c262dca60c17b5 to your computer and use it in GitHub Desktop.
NRF52 SDK15 update serial issue reproducer.
/* mbed Microcontroller Library
* Copyright (c) 2015 ARM Limited
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef BLE_CLIAPP_UTIL_CIRCULARBUFFER_H
#define BLE_CLIAPP_UTIL_CIRCULARBUFFER_H
#include <algorithm>
#include <stdint.h>
namespace util {
/** Templated Circular buffer class
*/
template<typename T, size_t BufferSize, typename CounterType = uint32_t>
class CircularBuffer {
public:
CircularBuffer() : _head(0), _tail(0), _full(false) {
}
/**
* Push the transaction to the buffer. This operation will fail if there is
* no room left in the buffer.
*
* @param data Data to be pushed to the buffer
* @return true if the operation succeed and false otherwise
*/
bool push(const T& data) {
if (full()) {
return false;
}
_buffer[_head] = data;
_head = incrementCounter(_head);
if (_head == _tail) {
_full = true;
}
return true;
}
/** Pop the transaction from the buffer
*
* @param data Data to be pushed to the buffer
* @return True if the buffer is not empty and data contains a transaction, false otherwise
*/
bool pop(T& data) {
if(empty()) {
return false;
}
data = _buffer[_tail];
_tail = incrementCounter(_tail);
_full = false;
return true;
}
/**
* @brief pop multiples elements from the buffer
*
* @param dest The array which will received the elements
* @param len The number of elements to pop
*
* @return The number of elements pop
*/
CounterType pop(T* dest, CounterType len) {
if(empty()) {
return 0;
}
if(_tail < _head) {
// truncation if the count if there is not enough elements available
if((_tail + len) > _head) {
len = _head - _tail;
}
std::copy(_buffer + _tail, _buffer + _tail + len, dest);
_tail += len;
return len;
} else {
if((_tail + len ) <= BufferSize) {
std::copy(_buffer + _tail, _buffer + _tail + len, dest);
_tail += len;
_tail = _tail % BufferSize;
_full = false;
return len;
} else {
// composition of previous operations
CounterType firstChunk = pop(dest, BufferSize - _tail);
return firstChunk + pop(dest + firstChunk, len - firstChunk);
}
}
}
/**
* @brief pop multiples elements from the buffer
*
* @param dest The array which will received the elements
*
* @return The number of elements pop
*/
template<CounterType N>
CounterType pop(T (&dest)[N]) {
return pop(dest, N);
}
/** Check if the buffer is empty
*
* @return True if the buffer is empty, false if not
*/
bool empty() const {
return (_head == _tail) && !_full;
}
/** Check if the buffer is full
*
* @return True if the buffer is full, false if not
*/
bool full() const {
return _full;
}
/**
* Reset the buffer
*/
void reset() {
_head = 0;
_tail = 0;
_full = false;
}
private:
CounterType incrementCounter(CounterType val) {
return (++val) % BufferSize;
}
T _buffer[BufferSize];
CounterType _head;
CounterType _tail;
bool _full;
};
} // namespace util
#endif /* BLE_CLIAPP_UTIL_CIRCULARBUFFER_H */
#include "events/EventQueue.h"
#include "drivers/RawSerial.h"
#include "CB.h"
#include "platform/mbed_error.h"
#include "platform/CriticalSectionLock.h"
/**
* Macros for setting console flow control.
*/
#define CONSOLE_FLOWCONTROL_RTS 1
#define CONSOLE_FLOWCONTROL_CTS 2
#define CONSOLE_FLOWCONTROL_RTSCTS 3
#define mbed_console_concat_(x) CONSOLE_FLOWCONTROL_##x
#define mbed_console_concat(x) mbed_console_concat_(x)
#define CONSOLE_FLOWCONTROL mbed_console_concat(MBED_CONF_TARGET_CONSOLE_UART_FLOW_CONTROL)
events::EventQueue event_queue;
// Prototypes
static void whenRxInterrupt(void);
static void consumeSerialBytes(void);
mbed::RawSerial& get_serial() {
static mbed::RawSerial serial(USBTX, USBRX);
static bool initialized = false;
if (!initialized) {
#if CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_RTS
serial.set_flow_control(SerialBase::RTS, STDIO_UART_RTS, NC);
#elif CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_CTS
serial.set_flow_control(SerialBase::CTS, NC, STDIO_UART_CTS);
#elif CONSOLE_FLOWCONTROL == CONSOLE_FLOWCONTROL_RTSCTS
serial.set_flow_control(SerialBase::RTSCTS, STDIO_UART_RTS, STDIO_UART_CTS);
#endif
initialized = true;
}
return serial;
}
// constants
static const size_t CIRCULAR_BUFFER_LENGTH = 768;
static const size_t CONSUMER_BUFFER_LENGTH = 32;
// circular buffer used by serial port interrupt to store characters
// It will be use in a single producer, single consumer setup:
// producer => RX interrupt
// consumer => a callback run by minar
static util::CircularBuffer<uint8_t, CIRCULAR_BUFFER_LENGTH> rxBuffer;
// callback called when a character arrive on the serial port
// this function will run in handler mode
static void whenRxInterrupt(void)
{
static mbed::RawSerial& serial = get_serial();
if (serial.readable()) {
bool startConsumer = rxBuffer.empty();
while (serial.readable()) {
if(rxBuffer.push((uint8_t) serial.getc()) == false) {
error("Not enough space in the circular buffer\r\n");
};
}
if(startConsumer) {
event_queue.call(consumeSerialBytes);
}
}
}
// consumptions of bytes from the serial port.
// this function should run in thread mode
static void consumeSerialBytes(void) {
// buffer of data
uint8_t data[CONSUMER_BUFFER_LENGTH];
uint32_t dataAvailable = 0;
bool shouldExit = false;
do {
{
mbed::CriticalSectionLock lock;
dataAvailable = rxBuffer.pop(data);
if(!dataAvailable) {
// sanity check, this should never happen
error("error, serial buffer is empty\r\n");
}
shouldExit = rxBuffer.empty();
}
mbed::RawSerial& serial = get_serial();
for (const uint8_t* c = data, *end = data + dataAvailable; c < end; ++c) {
serial.putc(*c);
}
} while(shouldExit == false);
}
int main(void)
{
//configure serial port
get_serial().baud(115200); // This is default baudrate for our test applications. 230400 is also working, but not 460800. At least with k64f.
get_serial().attach(whenRxInterrupt);
event_queue.dispatch_forever();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment