blinkenlights working w sonar
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
#include "main.h"
|
||||
|
||||
#include "com_fileio.h"
|
||||
#include "com_iface.h"
|
||||
#include "utils/str_utils.h"
|
||||
|
||||
// Holding fields for ifaces
|
||||
ComIface *debug_iface = NULL;
|
||||
ComIface *data_iface = NULL;
|
||||
|
||||
|
||||
// --- File descriptor names ------------------------------
|
||||
|
||||
struct name_fd {
|
||||
const char *name;
|
||||
const int fd;
|
||||
};
|
||||
|
||||
#define NAME_FD_MAP_LEN 1
|
||||
|
||||
/** pre-assigned file descriptors for names */
|
||||
static const struct name_fd name_fd_map[NAME_FD_MAP_LEN] = {
|
||||
{FNAME_DLNK, FD_DLNK}
|
||||
};
|
||||
|
||||
|
||||
// --- Syscalls -------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Write to a file by file descriptor.
|
||||
*
|
||||
* @param fd : open file descriptor
|
||||
* @param buf : data to write
|
||||
* @param len : buffer size
|
||||
* @return number of written bytes
|
||||
*/
|
||||
int _write(int fd, const char *buf, int len)
|
||||
{
|
||||
switch (fd) {
|
||||
case FD_STDOUT: return (int)com_tx_block(debug_iface, buf, (size_t)len);
|
||||
case FD_STDERR: return (int)com_tx_block(debug_iface, buf, (size_t)len);
|
||||
case FD_DLNK: return (int)com_tx_block(data_iface, buf, (size_t)len);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// For some reason, reading does not work.
|
||||
#if 0
|
||||
/**
|
||||
* @brief Read from a file descriptor
|
||||
*
|
||||
* @param fd : file descriptor
|
||||
* @param buf : destination buffer
|
||||
* @param len : number of bytes to read
|
||||
* @return actual number of read bytes
|
||||
*/
|
||||
int _read(int fd, char *buf, int len)
|
||||
{
|
||||
switch (fd) {
|
||||
case FD_STDIN: return com_rx_block(debug_iface, buf, len);
|
||||
case FD_ESP: return com_rx_block(esp_iface, buf, len);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* @brief Open a file by name.
|
||||
*
|
||||
* This stub is called by newlib when fopen is used.
|
||||
* It returns a pre-assigned file descriptor based
|
||||
* on the name.
|
||||
*
|
||||
* @note
|
||||
* stdout, stderr, stdin are open implicitly
|
||||
*
|
||||
* @param name : file name
|
||||
* @param flags : file flags (ignored)
|
||||
* @return file descriptor
|
||||
*/
|
||||
int _open(const char *name, int flags, ...)
|
||||
{
|
||||
(void)flags;
|
||||
|
||||
for (int i = 0; i < NAME_FD_MAP_LEN; i++) {
|
||||
if (streq(name_fd_map[i].name, name)) {
|
||||
return name_fd_map[i].fd;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include "com_iface.h"
|
||||
|
||||
/** Debug USART iface */
|
||||
extern ComIface *debug_iface;
|
||||
|
||||
/** ESP8266 com iface */
|
||||
extern ComIface *data_iface;
|
||||
|
||||
/** Do-nothing iface */
|
||||
extern ComIface *com_iface_noop;
|
||||
|
||||
|
||||
/** File descriptors for use with built-in "files" */
|
||||
enum {
|
||||
FD_STDIN = 0,
|
||||
FD_STDOUT = 1,
|
||||
FD_STDERR = 2,
|
||||
FD_DLNK = 3,
|
||||
};
|
||||
|
||||
#define FNAME_DLNK "dlnk"
|
||||
@@ -0,0 +1,179 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "com_iface.h"
|
||||
#include "utils/timebase.h"
|
||||
|
||||
// ---- accessor methods ----------------------
|
||||
|
||||
bool com_rx_rdy(ComIface *iface)
|
||||
{
|
||||
return iface->rx_rdy(iface);
|
||||
}
|
||||
|
||||
|
||||
bool com_tx_rdy(ComIface *iface)
|
||||
{
|
||||
return iface->tx_rdy(iface);
|
||||
}
|
||||
|
||||
|
||||
bool com_tx_done(ComIface *iface)
|
||||
{
|
||||
return iface->tx_done(iface);
|
||||
}
|
||||
|
||||
bool com_tx(ComIface *iface, uint8_t b)
|
||||
{
|
||||
return iface->tx(iface, b);
|
||||
}
|
||||
|
||||
|
||||
bool com_rx(ComIface *iface, uint8_t *b)
|
||||
{
|
||||
return iface->rx(iface, (uint8_t*) b);
|
||||
}
|
||||
|
||||
|
||||
bool com_unrx(ComIface *iface, uint8_t b)
|
||||
{
|
||||
if (!iface->unrx) {
|
||||
return false; // not all may have it implemented
|
||||
}
|
||||
return iface->unrx(iface, b);
|
||||
}
|
||||
|
||||
|
||||
size_t com_tx_block(ComIface *iface, const void *blob, size_t size)
|
||||
{
|
||||
return iface->txb(iface, blob, size);
|
||||
}
|
||||
|
||||
|
||||
size_t com_rx_block(ComIface *iface, void *buf, size_t length)
|
||||
{
|
||||
return iface->rxb(iface, buf, length);
|
||||
}
|
||||
|
||||
|
||||
void com_poll(ComIface *iface)
|
||||
{
|
||||
iface->poll(iface);
|
||||
}
|
||||
|
||||
|
||||
bool com_rx_char(ComIface *iface, char * c)
|
||||
{
|
||||
return iface->rx(iface, (uint8_t*) c);
|
||||
}
|
||||
|
||||
|
||||
bool com_tx_char(ComIface *iface, const char c)
|
||||
{
|
||||
return iface->tx(iface, (uint8_t)c);
|
||||
}
|
||||
|
||||
|
||||
/** Read string, terminate with \0. Returns real read size. */
|
||||
size_t com_rx_str(ComIface *iface, char* buf, size_t buflen)
|
||||
{
|
||||
size_t len = iface->rxb(iface, buf, buflen);
|
||||
buf[len] = 0; // zero terminator
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
size_t com_tx_str(ComIface *iface, const char * string)
|
||||
{
|
||||
return iface->txb(iface, string, strlen(string));
|
||||
}
|
||||
|
||||
|
||||
/** Wait for incoming byte */
|
||||
bool com_rx_wait(ComIface *iface, uint16_t timeout)
|
||||
{
|
||||
until_timeout(timeout) {
|
||||
if (iface->rx_rdy(iface)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** Wait for tx buf ready */
|
||||
bool com_tx_rdy_wait(ComIface *iface, uint16_t timeout)
|
||||
{
|
||||
until_timeout(timeout) {
|
||||
if (iface->tx_rdy(iface)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** Wait for tx complete */
|
||||
bool com_tx_done_wait(ComIface *iface, uint16_t timeout)
|
||||
{
|
||||
until_timeout(timeout) {
|
||||
if (iface->tx_done(iface)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void com_printf(ComIface *iface, const char *fmt, ...)
|
||||
{
|
||||
if (iface->file == NULL) {
|
||||
com_tx_str(iface, "com_printf(), no iface file!\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// use the file descriptor attached
|
||||
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
vfprintf(iface->file, fmt, va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
|
||||
void com_vprintf(ComIface *iface, const char *fmt, va_list va)
|
||||
{
|
||||
if (iface->file == NULL) {
|
||||
com_tx_str(iface, "com_vprintf(), no iface file!\r\n");
|
||||
com_tx_str(iface, fmt); // poor mans fallback
|
||||
return;
|
||||
}
|
||||
|
||||
// use the file descriptor attached
|
||||
vfprintf(iface->file, fmt, va);
|
||||
}
|
||||
|
||||
|
||||
void com_v100_attr_(ComIface *iface, uint8_t count, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, count);
|
||||
|
||||
com_tx_char(iface, 27);
|
||||
com_tx_char(iface, '[');
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
int attr = va_arg(va, int);
|
||||
|
||||
// comma
|
||||
if (i > 0) com_tx_char(iface, ';');
|
||||
|
||||
// number
|
||||
com_printf(iface, "%d", attr);
|
||||
}
|
||||
|
||||
com_tx_char(iface, 'm');
|
||||
|
||||
va_end(va);
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
// Implementation-independent UI (command handler)
|
||||
|
||||
typedef struct ComIface_struct ComIface;
|
||||
|
||||
struct ComIface_struct {
|
||||
// --- Variables ---
|
||||
|
||||
/** Implementation-specific data object */
|
||||
void *opts;
|
||||
|
||||
/** File descriptor for this stream */
|
||||
FILE *file;
|
||||
|
||||
/**
|
||||
* User callback for data ready, triggered on poll().
|
||||
* Can be null.
|
||||
*/
|
||||
void (*rx_callback)(ComIface *iface);
|
||||
|
||||
// --- Interface methods ---
|
||||
|
||||
/** Data ready to be read (RX buffer Not Empty) */
|
||||
bool (*rx_rdy)(ComIface *iface);
|
||||
|
||||
/** Free space in TX buffer */
|
||||
bool (*tx_rdy)(ComIface *iface);
|
||||
|
||||
/** Everything sent */
|
||||
bool (*tx_done)(ComIface *iface);
|
||||
|
||||
/** Send 1 byte. Blocking, timeout after some time. */
|
||||
bool (*tx)(ComIface *iface, uint8_t b);
|
||||
|
||||
/** Read one byte. False on failure. Blocking, timeout after some time. */
|
||||
bool (*rx)(ComIface *iface, uint8_t *b);
|
||||
|
||||
/** Unreceive one byte. False on failure. */
|
||||
bool (*unrx)(ComIface *iface, uint8_t b);
|
||||
|
||||
/** Send a binary block. False on failure. */
|
||||
size_t (*txb)(ComIface *iface, const void *blob, size_t size);
|
||||
|
||||
/** Read a binary block. Returns real read length. Blocking, timeout after some time. */
|
||||
size_t (*rxb)(ComIface *iface, void *buf, size_t length);
|
||||
|
||||
/** Poll for changes */
|
||||
void (*poll)(ComIface *iface);
|
||||
};
|
||||
|
||||
|
||||
// ---- Functions for working with iface --------------
|
||||
|
||||
/** Wait for incoming byte */
|
||||
bool com_rx_wait(ComIface *iface, uint16_t timeout);
|
||||
|
||||
/** Wait for free space in tx buffer */
|
||||
bool com_tx_rdy_wait(ComIface *iface, uint16_t timeout);
|
||||
|
||||
/** Wait for tx complete */
|
||||
bool com_tx_done_wait(ComIface *iface, uint16_t timeout);
|
||||
|
||||
/** Check if there's data in the receive buffer */
|
||||
bool com_rx_rdy(ComIface *iface);
|
||||
|
||||
/** Check if transmit buffer can accept data */
|
||||
bool com_tx_rdy(ComIface *iface);
|
||||
|
||||
/** Check if transmit buffer is empty */
|
||||
bool com_tx_done(ComIface *iface);
|
||||
|
||||
/** Send 1 byte */
|
||||
bool com_tx(ComIface *iface, uint8_t b);
|
||||
|
||||
/** Read one byte. False on failure. Fails on timeout. */
|
||||
bool com_rx(ComIface *iface, uint8_t *b);
|
||||
|
||||
/** Unrx one byte. False on failure. */
|
||||
bool com_unrx(ComIface *iface, uint8_t b);
|
||||
|
||||
/** Send a binary blob. False on failure. Fails on timeout. */
|
||||
size_t com_tx_block(ComIface *iface, const void *blob, size_t size);
|
||||
|
||||
/** Read a blob. Returns real read length. Stops on timeout. */
|
||||
size_t com_rx_block(ComIface *iface, void *buf, size_t length);
|
||||
|
||||
/** Poll for changes */
|
||||
void com_poll(ComIface *iface);
|
||||
|
||||
/** Send 1 char */
|
||||
bool com_tx_char(ComIface *iface, char c);
|
||||
|
||||
/** Read one char. False on failure. Fails on timeout. */
|
||||
bool com_rx_char(ComIface *iface, char *c);
|
||||
|
||||
/** Send a string. False on failure. */
|
||||
size_t com_tx_str(ComIface *iface, const char *str);
|
||||
|
||||
/** Read a string. Returns real read length. Stops on timeout. */
|
||||
size_t com_rx_str(ComIface *iface, char *buf, size_t length);
|
||||
|
||||
|
||||
/**
|
||||
* Printf to a serial interface.
|
||||
* Max length is 255 chars.
|
||||
*/
|
||||
void com_printf(ComIface *iface, const char *fmt, ...)
|
||||
__attribute__ ((format (printf, 2, 3)));
|
||||
|
||||
|
||||
/**
|
||||
* Printf to a serial interface, with va_list.
|
||||
*/
|
||||
void com_vprintf(ComIface *iface, const char *fmt, va_list va);
|
||||
|
||||
|
||||
// ---- VT100/ANSI color support -------------
|
||||
|
||||
/** ANSI formatting attributes */
|
||||
typedef enum {
|
||||
// Non-colour Attributes
|
||||
FMT_RESET = 0, // Reset all attributes
|
||||
FMT_BRIGHT = 1, // Bright
|
||||
FMT_DIM = 2, // Dim
|
||||
FMT_UNDER = 4, // Underscore
|
||||
FMT_BLINK = 5, // Blink
|
||||
FMT_INVERS = 7, // Reverse
|
||||
FMT_HIDDEN = 8, // Hidden
|
||||
FMT_ITALIC = 16, // Italic font
|
||||
FMT_FAINT = 32, // Faint color
|
||||
|
||||
// Foreground Colours
|
||||
FMT_BLACK = 30, // Black
|
||||
FMT_RED = 31, // Red
|
||||
FMT_GREEN = 32, // Green
|
||||
FMT_YELLOW = 33, // Yellow
|
||||
FMT_BLUE = 34, // Blue
|
||||
FMT_MAGENTA = 35, // Magenta
|
||||
FMT_CYAN = 36, // Cyan
|
||||
FMT_WHITE = 37, // White
|
||||
|
||||
// Background Colours
|
||||
FMT_BLACK_BG = 40, // Black
|
||||
FMT_RED_BG = 41, // Red
|
||||
FMT_GREEN_BG = 42, // Green
|
||||
FMT_YELLOW_BG = 43, // Yellow
|
||||
FMT_BLUE_BG = 44, // Blue
|
||||
FMT_MAGENTA_BG = 45, // Magenta
|
||||
FMT_CYAN_BG = 46, // Cyan
|
||||
FMT_WHITE_BG = 47, // White
|
||||
} ANSI_attr_t;
|
||||
|
||||
#define VA_NUM_ARGS(...) VA_NUM_ARGS_IMPL(__VA_ARGS__, 5,4,3,2,1)
|
||||
#define VA_NUM_ARGS_IMPL(_1,_2,_3,_4,_5,N,...) N
|
||||
|
||||
#define com_v100_attr(iface, ...) com_v100_attr_(iface, VA_NUM_ARGS(__VA_ARGS__), __VA_ARGS__)
|
||||
|
||||
/**
|
||||
* Send formatting code to a com interface
|
||||
*/
|
||||
void com_v100_attr_(ComIface *iface, uint8_t count, ...);
|
||||
@@ -0,0 +1,48 @@
|
||||
#include "datalink.h"
|
||||
#include "com_fileio.h"
|
||||
#include "debug.h"
|
||||
#include "com_fileio.h"
|
||||
|
||||
SBMP_Endpoint *dlnk_ep;
|
||||
|
||||
static void dlnk_tx(uint8_t b);
|
||||
static void dlnk_rx_bridge(ComIface *iface);
|
||||
|
||||
/** Set up the datalink */
|
||||
void dlnk_init(void)
|
||||
{
|
||||
// Allocate & setup the endpoint
|
||||
dlnk_ep = sbmp_ep_init(NULL, NULL, 100, dlnk_rx, dlnk_tx);
|
||||
sbmp_ep_seed_session(dlnk_ep, 0x3FFF); // just in case arbitration fails
|
||||
sbmp_ep_enable(dlnk_ep, true);
|
||||
|
||||
sbmp_ep_init_listeners(dlnk_ep, NULL, 4); // really don' need many here..
|
||||
|
||||
data_iface->rx_callback = dlnk_rx_bridge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bridge between USART subsystem's Rx buffer, and the SBMP driver
|
||||
*
|
||||
* Called if bytes are received in the USART buffer,
|
||||
* and the USART subsystem is polled.
|
||||
*/
|
||||
static void dlnk_rx_bridge(ComIface *iface)
|
||||
{
|
||||
uint8_t b;
|
||||
while (com_rx(iface, &b)) {
|
||||
SBMP_RxStatus st = sbmp_ep_receive(dlnk_ep, b);
|
||||
|
||||
// If byte was not accepted, try to put it back into the buffer
|
||||
if (st == SBMP_RX_BUSY || st == SBMP_RX_DISABLED) {
|
||||
com_unrx(iface, b);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Datalink Tx func */
|
||||
static void dlnk_tx(uint8_t b)
|
||||
{
|
||||
com_tx(data_iface, b);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* SBMP setup & funcs
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
#include <sbmp.h>
|
||||
|
||||
#define DG_REQUEST_RAW 40 // request raw vector. Sample count [u16], Frequency [u32]
|
||||
#define DG_REQUEST_FFT 41 // request fft vector. Sample count [u16], Frequency [u32]. Result - count/2 bins. Count must be 2^n, 16..2048
|
||||
#define DG_REQUEST_STORE_REF 42 // calculate signal signature & store for comparing
|
||||
#define DG_REQUEST_COMPARE_REF 43
|
||||
// wifi status & control
|
||||
#define DG_SETMODE_AP 44 // request AP mode (AP button pressed)
|
||||
#define DG_WPS_START 45 // start WPS
|
||||
#define DG_WIFI_STATUS 46 // WiFi status report
|
||||
#define DG_REQUEST_STM_VERSION 47 // Get acquisition module firmware version
|
||||
|
||||
|
||||
extern SBMP_Endpoint *dlnk_ep;
|
||||
|
||||
void dlnk_init(void);
|
||||
|
||||
/** Received datagram handler */
|
||||
extern void dlnk_rx(SBMP_Datagram *dg);
|
||||
@@ -0,0 +1,108 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "com_iface.h"
|
||||
|
||||
#include "utils/timebase.h"
|
||||
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
void dbg_printf(const char *fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
com_vprintf(debug_iface, fmt, va); // vsnprintf(strbuf, DBG_BUF_LEN, fmt, va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
|
||||
void dbg_va_base(const char *fmt, const char *tag, va_list va)
|
||||
{
|
||||
ms_time_t now = ms_now();
|
||||
uint32_t secs = now / 1000;
|
||||
uint32_t ms = now % 1000;
|
||||
|
||||
com_printf(debug_iface, "%4"PRIu32".%03"PRIu32" ", secs, ms);
|
||||
|
||||
dbg_raw(tag);
|
||||
|
||||
com_vprintf(debug_iface, fmt, va);
|
||||
dbg_raw(DEBUG_EOL);
|
||||
}
|
||||
|
||||
/** Print a log message with an INFO tag and newline (ONLY FOR BANNER - always shown) */
|
||||
void banner_info(const char *fmt, ...)
|
||||
{
|
||||
com_v100_attr(debug_iface, FMT_GREEN);
|
||||
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
dbg_va_base(fmt, DEBUG_TAG_INFO, va);
|
||||
va_end(va);
|
||||
|
||||
com_v100_attr(debug_iface, FMT_RESET);
|
||||
}
|
||||
|
||||
|
||||
#if VERBOSE_LOGGING
|
||||
|
||||
/** Print a log message with a DEBUG tag and newline */
|
||||
void dbg(const char *fmt, ...)
|
||||
{
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
dbg_va_base(fmt, DEBUG_TAG_BASE, va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
|
||||
/** Print a log message with an INFO tag and newline */
|
||||
void info(const char *fmt, ...) __attribute__((alias("banner_info")));
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/** Print a log message with an INFO tag and newline */
|
||||
void banner(const char *fmt, ...)
|
||||
{
|
||||
com_v100_attr(debug_iface, FMT_GREEN, FMT_BRIGHT);
|
||||
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
dbg_va_base(fmt, DEBUG_TAG_INFO, va);
|
||||
va_end(va);
|
||||
|
||||
com_v100_attr(debug_iface, FMT_RESET);
|
||||
}
|
||||
|
||||
|
||||
/** Print a log message with a warning tag and newline */
|
||||
void warn(const char *fmt, ...)
|
||||
{
|
||||
com_v100_attr(debug_iface, FMT_YELLOW, FMT_BRIGHT);
|
||||
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
dbg_va_base(fmt, DEBUG_TAG_WARN, va);
|
||||
va_end(va);
|
||||
|
||||
com_v100_attr(debug_iface, FMT_RESET);
|
||||
}
|
||||
|
||||
|
||||
/** Print a log message with an ERROR tag and newline */
|
||||
void error(const char *fmt, ...)
|
||||
{
|
||||
com_v100_attr(debug_iface, FMT_RED, FMT_BRIGHT);
|
||||
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
dbg_va_base(fmt, DEBUG_TAG_ERROR, va);
|
||||
va_end(va);
|
||||
|
||||
com_v100_attr(debug_iface, FMT_RESET);
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include "main.h"
|
||||
#include "com_iface.h"
|
||||
#include "com_fileio.h"
|
||||
|
||||
#include "bus/event_queue.h"
|
||||
|
||||
// helper to mark printf functions
|
||||
#define PRINTF_LIKE __attribute__((format(printf, 1, 2)))
|
||||
|
||||
#define DBG_BUF_LEN 256
|
||||
|
||||
#define ESCAPE_DEBUG_MESSAGES 1
|
||||
|
||||
|
||||
// formatting symbols
|
||||
#define DEBUG_EOL "\r\n"
|
||||
#define DEBUG_TAG_WARN "[W] "
|
||||
#define DEBUG_TAG_ERROR "[E] "
|
||||
#define DEBUG_TAG_BASE "[ ] "
|
||||
#define DEBUG_TAG_INFO "[i] "
|
||||
|
||||
|
||||
/** Print a log message with no tag and no newline */
|
||||
void dbg_printf(const char *fmt, ...) PRINTF_LIKE;
|
||||
|
||||
/** Print via va_list */
|
||||
void dbg_va_base(const char *fmt, const char *tag, va_list va);
|
||||
|
||||
/** Print a string to the debug interface (length not limited) */
|
||||
static inline void dbg_raw(const char *str)
|
||||
{
|
||||
com_tx_str(debug_iface, str);
|
||||
}
|
||||
|
||||
|
||||
/** Print a char to the debug interface */
|
||||
static inline void dbg_raw_c(char c)
|
||||
{
|
||||
com_tx(debug_iface, (uint8_t)c);
|
||||
}
|
||||
|
||||
|
||||
#if VERBOSE_LOGGING
|
||||
|
||||
/** Print a log message with a "debug" tag and newline */
|
||||
void dbg(const char *fmt, ...) PRINTF_LIKE;
|
||||
|
||||
|
||||
/** Print a log message with an "info" tag and newline */
|
||||
void info(const char *fmt, ...) PRINTF_LIKE;
|
||||
|
||||
#else
|
||||
|
||||
#define dbg(fmt, ...)
|
||||
#define info(fmt, ...)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/** Print a log message with an "info" tag and newline */
|
||||
void banner_info(const char *fmt, ...) PRINTF_LIKE;
|
||||
|
||||
/** Print a log message with a "banner" tag and newline */
|
||||
void banner(const char *fmt, ...) PRINTF_LIKE;
|
||||
|
||||
/** Print a log message with a "warning" tag and newline */
|
||||
void warn(const char *fmt, ...) PRINTF_LIKE;
|
||||
|
||||
|
||||
/** Print a log message with an "error" tag and newline */
|
||||
void error(const char *fmt, ...) PRINTF_LIKE;
|
||||
@@ -0,0 +1,91 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "iface_noop.h"
|
||||
#include "com_fileio.h"
|
||||
#include "malloc_safe.h"
|
||||
|
||||
// ==== NOOP com driver ====
|
||||
|
||||
static bool if_rx_rdy(ComIface *iface)
|
||||
{
|
||||
(void)iface;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool if_tx_rdy(ComIface *iface)
|
||||
{
|
||||
(void)iface;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool if_tx_done(ComIface *iface)
|
||||
{
|
||||
(void)iface;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool if_rx(ComIface *iface, uint8_t *b)
|
||||
{
|
||||
(void)iface;
|
||||
(void)b;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool if_unrx(ComIface *iface, uint8_t b)
|
||||
{
|
||||
(void)iface;
|
||||
(void)b;
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool if_tx(ComIface *iface, uint8_t b)
|
||||
{
|
||||
(void)iface;
|
||||
(void)b;
|
||||
return true;
|
||||
}
|
||||
|
||||
static size_t if_rxb(ComIface *iface, void *buf, size_t buflen)
|
||||
{
|
||||
(void)iface;
|
||||
(void)buf;
|
||||
(void)buflen;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t if_txb(ComIface *iface, const void *blob, size_t size)
|
||||
{
|
||||
(void)iface;
|
||||
(void)blob;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static void if_poll(ComIface *iface)
|
||||
{
|
||||
(void)iface;
|
||||
}
|
||||
|
||||
ComIface *com_noop_init(void)
|
||||
{
|
||||
ComIface *iface = malloc_s(sizeof(ComIface));
|
||||
|
||||
iface->rx_rdy = if_rx_rdy;
|
||||
iface->tx_rdy = if_tx_rdy;
|
||||
iface->tx_done = if_tx_done;
|
||||
|
||||
iface->rx = if_rx;
|
||||
iface->unrx = if_unrx;
|
||||
iface->tx = if_tx;
|
||||
|
||||
iface->rxb = if_rxb;
|
||||
iface->txb = if_txb;
|
||||
|
||||
iface->poll = if_poll;
|
||||
iface->rx_callback = NULL;
|
||||
|
||||
return iface;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* NOOP iface works like /dev/null
|
||||
*/
|
||||
|
||||
#include "com_iface.h"
|
||||
|
||||
/**
|
||||
* @brief Initialize a do-nothing interface.
|
||||
* @param iface : iface pointer. If NULL, it'll be allocated.
|
||||
* @return the iface pointer.
|
||||
*/
|
||||
ComIface *com_noop_init(void);
|
||||
@@ -0,0 +1,328 @@
|
||||
#include "main.h"
|
||||
#include "malloc_safe.h"
|
||||
#include "iface_usart.h"
|
||||
|
||||
#include "utils/timebase.h"
|
||||
#include "utils/circbuf.h"
|
||||
|
||||
#include "com/com_fileio.h"
|
||||
#include "com/debug.h"
|
||||
|
||||
#include "bus/event_queue.h"
|
||||
|
||||
|
||||
#define DEF_WAIT_TO_MS 5
|
||||
|
||||
|
||||
typedef struct {
|
||||
uint16_t wait_timeout;
|
||||
USART_TypeDef *dev;
|
||||
CircBuf *rxbuf; // allocated receive buffer
|
||||
CircBuf *txbuf; // allocated transmit buffer, can be NULL -> no buffer
|
||||
} usart_opts;
|
||||
|
||||
#define opts(iface) ((usart_opts *)(iface)->opts)
|
||||
|
||||
// ---- Instances ----
|
||||
// (needed for async rx/tx with interrupts)
|
||||
|
||||
static ComIface *usart1_iface = NULL;
|
||||
static ComIface *usart2_iface = NULL;
|
||||
static ComIface *usart3_iface = NULL;
|
||||
|
||||
// -------------------
|
||||
|
||||
|
||||
/** Check if data is ready for reading */
|
||||
static bool if_rx_rdy(ComIface *iface)
|
||||
{
|
||||
CircBuf *buf = opts(iface)->rxbuf;
|
||||
|
||||
return !cbuf_empty(buf);
|
||||
}
|
||||
|
||||
|
||||
/** Check if sending is done */
|
||||
static bool if_tx_rdy(ComIface *iface)
|
||||
{
|
||||
CircBuf *buf = opts(iface)->txbuf;
|
||||
|
||||
if (buf == NULL) {
|
||||
// non-buffered mode
|
||||
USART_TypeDef* USARTx = opts(iface)->dev;
|
||||
return (USARTx->SR & USART_SR_TXE);
|
||||
}
|
||||
|
||||
return !cbuf_full(buf);
|
||||
}
|
||||
|
||||
|
||||
/** Check if sending is done */
|
||||
static bool if_tx_done(ComIface *iface)
|
||||
{
|
||||
CircBuf *buf = opts(iface)->txbuf;
|
||||
USART_TypeDef* USARTx = opts(iface)->dev;
|
||||
|
||||
// NULL buffer is considered empty
|
||||
return cbuf_empty(buf) && (USARTx->SR & USART_SR_TC);
|
||||
}
|
||||
|
||||
|
||||
/** Read one byte (wait for it). False on error. */
|
||||
static bool if_rx(ComIface *iface, uint8_t *b)
|
||||
{
|
||||
// wait for data in the buffer
|
||||
if (!com_rx_wait(iface, opts(iface)->wait_timeout) || b == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// read from buffer
|
||||
cbuf_pop(opts(iface)->rxbuf, b);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** Try to unreceive a byte. False on error. */
|
||||
static bool if_unrx(ComIface *iface, uint8_t b)
|
||||
{
|
||||
// push back
|
||||
return cbuf_push(opts(iface)->rxbuf, &b);
|
||||
}
|
||||
|
||||
|
||||
/** Send one byte (waits for tx) */
|
||||
static bool if_tx(ComIface *iface, uint8_t b)
|
||||
{
|
||||
usart_opts *uopts = opts(iface);
|
||||
USART_TypeDef* USARTx = uopts->dev;
|
||||
|
||||
if (uopts->txbuf == NULL) {
|
||||
|
||||
// Blocking tx mode
|
||||
until_timeout(uopts->wait_timeout) {
|
||||
if (USARTx->SR & USART_SR_TXE) {
|
||||
USARTx->DR = b;
|
||||
return true; // success
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
} else {
|
||||
|
||||
// Buffered mode
|
||||
|
||||
// wait for free space in the buffer
|
||||
bool ready = com_tx_rdy_wait(iface, uopts->wait_timeout);
|
||||
|
||||
if (ready) {
|
||||
// append to the buffer
|
||||
cbuf_append(uopts->txbuf, &b);
|
||||
}
|
||||
|
||||
// regardless ready state, start Tx if there are bytes
|
||||
// (should fix a bug where full buffer was never emptied)
|
||||
|
||||
// If TXE (send buffer empty), start sending the buffer
|
||||
// Otherwise, IRQ chain is in progress.
|
||||
if (USARTx->SR & USART_SR_TXE) {
|
||||
USART_ITConfig(USARTx, USART_IT_TXE, ENABLE); // start tx chain
|
||||
}
|
||||
|
||||
return ready;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Read a blob. Returns real read size */
|
||||
static size_t if_rxb(ComIface *iface, void *buf, size_t buflen)
|
||||
{
|
||||
if (buf == NULL) return false;
|
||||
//if (!com_rx_wait(iface, opts(iface)->wait_timeout)) return 0;
|
||||
|
||||
uint8_t* byteptr = (uint8_t*)buf;
|
||||
for (size_t i = 0; i < buflen; i++) {
|
||||
if (!if_rx(iface, byteptr++)) return i;
|
||||
}
|
||||
|
||||
return buflen;
|
||||
}
|
||||
|
||||
|
||||
/** Send a binary blob. False on error. */
|
||||
static size_t if_txb(ComIface *iface, const void *blob, size_t size)
|
||||
{
|
||||
if (blob == NULL) return false;
|
||||
//if (!com_tx_rdy_wait(iface, opts(iface)->wait_timeout)) return false;
|
||||
|
||||
const uint8_t* byteptr = (const uint8_t*)blob;
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
if (!if_tx(iface, *byteptr++)) return i;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
/** Check for incoming data */
|
||||
static void if_poll(ComIface *iface)
|
||||
{
|
||||
if (if_rx_rdy(iface)) {
|
||||
// call user cb
|
||||
if (iface->rx_callback) {
|
||||
iface->rx_callback(iface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ---- Init interface ----
|
||||
|
||||
//void usart_iface_set_baudrate(ComIface *iface, uint32_t baud)
|
||||
//{
|
||||
// USART_TypeDef* USARTx = opts(iface)->dev;
|
||||
//
|
||||
// USART_SetBaudrate(USARTx, baud);
|
||||
//}
|
||||
|
||||
|
||||
/* public */
|
||||
ComIface *usart_iface_init(USART_TypeDef* USARTx, uint32_t baud, size_t rxbuf_len, size_t txbuf_len)
|
||||
{
|
||||
assert_param(IS_USART_BAUDRATE(baud));
|
||||
assert_param(rxbuf_len > 0);
|
||||
|
||||
ComIface *iface = malloc_s(sizeof(ComIface));
|
||||
|
||||
// --- setup device specific iface data ---
|
||||
|
||||
// Allocate the opts config object
|
||||
iface->opts = malloc_s(sizeof(usart_opts));
|
||||
|
||||
// Set device ID
|
||||
opts(iface)->dev = USARTx;
|
||||
opts(iface)->wait_timeout = DEF_WAIT_TO_MS;
|
||||
|
||||
// Initialize the rx/tx buffers (malloc's the array)
|
||||
opts(iface)->rxbuf = cbuf_create(rxbuf_len, 1);
|
||||
|
||||
// zero-length TX buffer -> blocking Tx
|
||||
opts(iface)->txbuf = (txbuf_len == 0) ? NULL : cbuf_create(txbuf_len, 1);
|
||||
|
||||
// --- driver instance ---
|
||||
|
||||
iface->rx_rdy = if_rx_rdy;
|
||||
iface->tx_rdy = if_tx_rdy;
|
||||
iface->tx_done = if_tx_done;
|
||||
|
||||
iface->rx = if_rx;
|
||||
iface->unrx = if_unrx;
|
||||
iface->tx = if_tx;
|
||||
|
||||
iface->txb = if_txb;
|
||||
iface->rxb = if_rxb;
|
||||
|
||||
iface->poll = if_poll;
|
||||
|
||||
iface->rx_callback = NULL; // user can replace
|
||||
|
||||
|
||||
/* enable peripehral clock, assign to holder var, enable IRQ */
|
||||
IRQn_Type irqn;
|
||||
|
||||
if (USARTx == USART1) {
|
||||
// USART1
|
||||
usart1_iface = iface;
|
||||
irqn = USART1_IRQn;
|
||||
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
|
||||
|
||||
} else if (USARTx == USART2) {
|
||||
// USART2
|
||||
usart2_iface = iface;
|
||||
irqn = USART2_IRQn;
|
||||
RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
|
||||
|
||||
} else {
|
||||
// USART3
|
||||
usart3_iface = iface;
|
||||
irqn = USART3_IRQn;
|
||||
RCC->APB1ENR |= RCC_APB1ENR_USART3EN;
|
||||
}
|
||||
|
||||
// Enable IRQ
|
||||
NVIC_EnableIRQ(irqn);
|
||||
|
||||
// lower priority than SysTick, but high
|
||||
NVIC_SetPriority(irqn, 1); // function does the shifting
|
||||
|
||||
|
||||
/* Setup UART parameters. */
|
||||
USART_InitTypeDef usart;
|
||||
USART_StructInit(&usart);
|
||||
usart.USART_BaudRate = baud;
|
||||
USART_Init(USARTx, &usart);
|
||||
|
||||
/* Enable */
|
||||
USART_Cmd(USARTx, ENABLE);
|
||||
|
||||
// Enable Rx interrupt
|
||||
USART_ITConfig(USARTx, USART_IT_RXNE, ENABLE); // disable IRQ
|
||||
|
||||
return iface;
|
||||
}
|
||||
|
||||
|
||||
// ---- IRQ handlers for chained writing and rx ----
|
||||
|
||||
|
||||
/**
|
||||
* @brief Common USART irq handler
|
||||
* @param iface the interface at which the event occured
|
||||
*/
|
||||
static void usart_iface_irq_base(ComIface* iface)
|
||||
{
|
||||
USART_TypeDef* USARTx = opts(iface)->dev;
|
||||
|
||||
if (USARTx->SR & USART_SR_RXNE) {
|
||||
// Byte received.
|
||||
uint8_t rxb = USARTx->DR & 0xFF;
|
||||
if (!cbuf_append(opts(iface)->rxbuf, &rxb)) {
|
||||
// Buffer overflow
|
||||
// Can't print debug msg, can cause deadlock
|
||||
}
|
||||
}
|
||||
|
||||
if (USARTx->SR & USART_SR_TXE) {
|
||||
// Byte sent.
|
||||
uint8_t txb;
|
||||
|
||||
// Send next byte (if buffer is NULL, cbuf_pop also returns false)
|
||||
if (cbuf_pop(opts(iface)->txbuf, &txb)) {
|
||||
USARTx->DR = txb; // send data
|
||||
} else {
|
||||
USART_ITConfig(USARTx, USART_IT_TXE, DISABLE); // disable IRQ
|
||||
}
|
||||
}
|
||||
|
||||
// Clear ORE flag if set
|
||||
USART_ClearFlag(USARTx, USART_FLAG_ORE);
|
||||
}
|
||||
|
||||
|
||||
void USART1_IRQHandler(void)
|
||||
{
|
||||
usart_iface_irq_base(usart1_iface);
|
||||
}
|
||||
|
||||
|
||||
void USART2_IRQHandler(void)
|
||||
{
|
||||
usart_iface_irq_base(usart2_iface);
|
||||
}
|
||||
|
||||
|
||||
void USART3_IRQHandler(void)
|
||||
{
|
||||
usart_iface_irq_base(usart3_iface);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "main.h"
|
||||
#include "com_iface.h"
|
||||
|
||||
// Hardware USART.
|
||||
|
||||
/**
|
||||
* @brief Init an USART interface. Caller must configure GPIO's & AF manually.
|
||||
* @param USARTx device
|
||||
* @param baud baud rate (ex.: 9600)
|
||||
* @param rxbuf_len receive buffer size, must be > 0
|
||||
* @param txbuf_len send buffer size. If 0, tx is blocking.
|
||||
* @return the iface structure
|
||||
*/
|
||||
ComIface *usart_iface_init(USART_TypeDef* USARTx, uint32_t baud, size_t rxbuf_len, size_t txbuf_len);
|
||||
|
||||
///** Set baud rate */
|
||||
//void usart_iface_set_baudrate(ComIface *iface, uint32_t baud);
|
||||
Reference in New Issue
Block a user