Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 162 additions & 0 deletions lib/cionic/ringbuf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
#include "shared-bindings/util.h"
#include "py/gc.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdatomic.h>
#include "shared/runtime/interrupt_char.h"

#include "ringbuf.h"

ringbuf_t *cionic_ringbuf_alloc(uint16_t sample_size, uint16_t nsamples)
{
int buflen = nsamples*sample_size;
ringbuf_t *rb = NULL;

rb = gc_alloc((sizeof(ringbuf_t) + buflen), false);
// ** removed the third param 'long_lived'=true from gc_alloc call (was included in our v7 code) because it is not supported in CP v9 anymore
// 'false' flag is for alloc_flags instead (consistent with v7 code)

if(rb == NULL){
return NULL;
}

rb->cbuflen = buflen;
rb->sample_size = sample_size;
rb->write_idx = rb->read_idx = 0;
atomic_flag_clear(&rb->lock);

return rb;
}

void cionic_ringbuf_lock(ringbuf_t *rb) {
while (atomic_flag_test_and_set(&rb->lock)) {
// spinning... no OS or threading support for full mutex in circuitpython
mp_handle_pending(true);
// Allow user to break out of a timeout with a KeyboardInterrupt.
if (mp_hal_is_interrupted()) {
return;
}
}
}

void cionic_ringbuf_unlock(ringbuf_t *rb) {
atomic_flag_clear(&rb->lock);
}

void cionic_ringbuf_clear(ringbuf_t *rb)
{
cionic_ringbuf_lock(rb);
rb->write_idx = rb->read_idx = 0;
cionic_ringbuf_unlock(rb);
}

int cionic_ringbuf_len(ringbuf_t *rb)
{
int len;
cionic_ringbuf_lock(rb);
len = (rb->write_idx + rb->cbuflen - rb->read_idx) % rb->cbuflen;
cionic_ringbuf_unlock(rb);
return len;
}

// locked read
// fill buf with as many whole samples as will fit; return number of bytes written into buf
int cionic_ringbuf_read_samples(ringbuf_t *rb, void *_buf, int buflen)
{
int ret;
cionic_ringbuf_lock(rb);
ret = cionic_ringbuf_read_samples_nolock(rb, _buf, buflen);
cionic_ringbuf_unlock(rb);
return ret;
}

// locked write
// write all buflen bytes from buf into ringbuf, or return false
bool cionic_ringbuf_write_sample(ringbuf_t *rb, const void *_buf, int buflen)
{
bool ret;
cionic_ringbuf_lock(rb);
ret = cionic_ringbuf_write_sample_nolock(rb, _buf, buflen);
cionic_ringbuf_unlock(rb);
return ret;
}

// nolock read
// fill buf with as many whole samples as will fit; return number of bytes written into buf
int cionic_ringbuf_read_samples_nolock(ringbuf_t *rb, void *_buf, int buflen)
{
uint8_t *buf = _buf;
const int nsamples = buflen / rb->sample_size;
const int copylen = nsamples * rb->sample_size;

// bytes in buffer waiting to be read
int backavail = 0; // nbytes from read_idx
int frontavail = 0; // nbytes from 0 to write_idx

if (rb->write_idx >= rb->read_idx) {
// from read_idx to write_idx
backavail = rb->write_idx - rb->read_idx;
frontavail = 0;
} else {
// from read_idx to end of buf, and then from 0 to write_idx
backavail = rb->cbuflen - rb->read_idx;
frontavail = rb->write_idx;
}

int backlen = MIN(backavail, copylen);
memcpy(buf, &rb->cbuf[rb->read_idx], backlen);

int frontlen = MIN(frontavail, copylen-backlen);
if (frontlen > 0) {
memcpy(buf+backlen, &rb->cbuf[0], frontlen);
} else {
frontlen = 0;
}

rb->read_idx = (rb->read_idx + frontlen + backlen) % rb->cbuflen;

return backlen + frontlen;
}

// nolock write
// write all buflen bytes from buf into ringbuf, or return false
bool cionic_ringbuf_write_sample_nolock(ringbuf_t *rb, const void *_buf, int buflen)
{
const uint8_t *buf = _buf;

// space available in rb
int backavail = 0; // nbytes from write_idx
int frontavail = 0; // nbytes from 0
if (rb->write_idx >= rb->read_idx) {
// from write_idx to end of buf and then from 0 to read_idx-1
backavail = rb->cbuflen - rb->write_idx;
frontavail = rb->read_idx - 1;
} else {
// from write_idx to read_idx-1
backavail = rb->read_idx - rb->write_idx - 1;
frontavail = 0;
}

if (backavail + frontavail < rb->sample_size) {
// not enough space
return false;
}

int write_idx = rb->write_idx;

int backlen = MIN(backavail, rb->sample_size);
memcpy(&rb->cbuf[write_idx], buf, backlen);

int frontlen = MIN(frontavail, rb->sample_size - backlen);
if (frontlen > 0) {
memcpy(&rb->cbuf[0], buf+backlen, frontlen);
}

// set write_idx last for thread immunity
// consider lock around read and writes for greater protection
rb->write_idx = (rb->write_idx + rb->sample_size) % rb->cbuflen;

return true;
}

45 changes: 45 additions & 0 deletions lib/cionic/ringbuf.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#ifndef CIONIC_RINGBUF_H_
#define CIONIC_RINGBUF_H_

#include <stdatomic.h>

typedef struct ringbuf_t {
uint16_t cbuflen;
uint16_t sample_size;
uint16_t write_idx;
uint16_t read_idx;
atomic_flag lock; // use atomic for lock
uint8_t cbuf[];
} ringbuf_t;

// allocate new ringbuf
ringbuf_t *cionic_ringbuf_alloc(uint16_t sample_size, uint16_t nsamples);

// lock/unlock ringbuf
void cionic_ringbuf_lock(ringbuf_t *rb);
void cionic_ringbuf_unlock(ringbuf_t *rb);

// clear any existing data in ringbuf
void cionic_ringbuf_clear(ringbuf_t *rb);

// return nbytes in ringbuf available to be read
int cionic_ringbuf_len(ringbuf_t *rb);

// locked read; fill buf with as many whole samples as will fit; return number of bytes written into buf
int cionic_ringbuf_read_samples(ringbuf_t *rb, void *buf, int buflen);

// locked write; write all buflen bytes from buf into ringbuf, or return false
bool cionic_ringbuf_write_sample(ringbuf_t *rb, const void *buf, int buflen);

// nolock read; fill buf with as many whole samples as will fit; return number of bytes written into buf
int cionic_ringbuf_read_samples_nolock(ringbuf_t *rb, void *buf, int buflen);

// nolock write; write all buflen bytes from buf into ringbuf, or return false
bool cionic_ringbuf_write_sample_nolock(ringbuf_t *rb, const void *buf, int buflen);

// return number of samples in buffer
static inline int cionic_ringbuf_num_samples(ringbuf_t *rb) {
return cionic_ringbuf_len(rb)/rb->sample_size;
}

#endif
1 change: 1 addition & 0 deletions ports/nordic/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ ifeq ($(CIONIC_LIB), 1)
INC += -I../../lib/cionic
SRC_C += ../../lib/cionic/diff_filter.c
SRC_C += ../../lib/cionic/emg_iir.c
SRC_C += ../../lib/cionic/ringbuf.c
SRC_C += ../../lib/cionic/orientation.c
endif

Expand Down
22 changes: 12 additions & 10 deletions shared-bindings/ads1x9x/ADS1x9x.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@
//| class ADS1x9x:
//| """ADS1x9x Interface
//|
//| Interacts with an ADS1x9x over SPI."""
//| Interacts with an ADS1x9x over SPI.
//| """
//|
//| def __init__(
//| self,
Expand All @@ -51,7 +52,8 @@
//| start: microcontroller.Pin,
//| pwdn: microcontroller.Pin,
//| ) -> None:
//| """Construct an SPI ADS1x9x object with the given properties
//| """
//| Construct an SPI ADS1x9x object with the given properties.
//|
//| :param busio.SPI spi: The SPI bus
//| :param microcontroller.Pin cs: The SPI chip select
Expand Down Expand Up @@ -102,7 +104,7 @@ STATIC mp_obj_t ads1x9x_ads1x9x_make_new(const mp_obj_type_t *type, size_t n_arg
}

//| def reset(self) -> None:
//| """Reset the ADS1x9x
//| """Reset the ADS1x9x.
//|
//| :return: None"""
STATIC mp_obj_t ads1x9x_ads1x9x_reset(mp_obj_t self_in) {
Expand All @@ -113,7 +115,7 @@ STATIC mp_obj_t ads1x9x_ads1x9x_reset(mp_obj_t self_in) {
MP_DEFINE_CONST_FUN_OBJ_1(ads1x9x_ads1x9x_reset_obj, ads1x9x_ads1x9x_reset);

//| def sample_size_get(self) -> None:
//| """Get the ADS1x9x sample size
//| """Get the ADS1x9x sample size.
//|
//| :return: Sample size"""
STATIC mp_obj_t ads1x9x_ads1x9x_sample_size_get(mp_obj_t self_in) {
Expand All @@ -123,7 +125,7 @@ STATIC mp_obj_t ads1x9x_ads1x9x_sample_size_get(mp_obj_t self_in) {
MP_DEFINE_CONST_FUN_OBJ_1(ads1x9x_ads1x9x_sample_size_get_obj, ads1x9x_ads1x9x_sample_size_get);

//| def filter_set(self, filter) -> None:
//| """Set filter type for ADS1x9x
//| """Set filter type for ADS1x9x.
//|
//| :param int filter: The filter enum to write
//| :return: None"""
Expand All @@ -137,7 +139,7 @@ STATIC mp_obj_t ads1x9x_ads1x9x_filter_set(mp_obj_t self_in, mp_obj_t filter) {
MP_DEFINE_CONST_FUN_OBJ_2(ads1x9x_ads1x9x_filter_set_obj, ads1x9x_ads1x9x_filter_set);

//| def read_reg(self, address) -> int:
//| """Read a ADS1x9x register
//| """Read a ADS1x9x register.
//|
//| :param int address: The register address to read from
//| :return: register value"""
Expand All @@ -150,7 +152,7 @@ STATIC mp_obj_t ads1x9x_ads1x9x_read_reg(mp_obj_t self_in, mp_obj_t reg_addr) {
MP_DEFINE_CONST_FUN_OBJ_2(ads1x9x_ads1x9x_read_reg_obj, ads1x9x_ads1x9x_read_reg);

//| def write_reg(self, address, value) -> None:
//| """Write value to a ADS1x9x register
//| """Write value to a ADS1x9x register.
//|
//| :param int address: The register address to write to
//| :param int value: The value address to write
Expand All @@ -168,7 +170,7 @@ MP_DEFINE_CONST_FUN_OBJ_3(ads1x9x_ads1x9x_write_reg_obj, ads1x9x_ads1x9x_write_r


//| def start(self) -> None:
//| """Start ADS1x9x sampling
//| """Start ADS1x9x sampling.
//|
//| :return: None"""

Expand All @@ -182,7 +184,7 @@ STATIC mp_obj_t ads1x9x_ads1x9x_start(mp_obj_t self_in) {
MP_DEFINE_CONST_FUN_OBJ_1(ads1x9x_ads1x9x_start_obj, ads1x9x_ads1x9x_start);

//| def stop(self) -> None:
//| """Stop ADS1x9x sampling
//| """Stop ADS1x9x sampling.
//|
//| :return: None"""

Expand All @@ -196,7 +198,7 @@ STATIC mp_obj_t ads1x9x_ads1x9x_stop(mp_obj_t self_in) {
MP_DEFINE_CONST_FUN_OBJ_1(ads1x9x_ads1x9x_stop_obj, ads1x9x_ads1x9x_stop);

//| def read(self, buffer) -> int:
//| """Read ADS1x9x data
//| """Read ADS1x9x data.
//|
//| :param buffer: Buffer to write data to
//| :return: size read"""
Expand Down
Loading
Loading