Compare commits
9 Commits
refactorin
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| a54f15bf5f | |||
| 0fcbbf06bf | |||
| dad3d7d48d | |||
| 919523a465 | |||
| 3688db710a | |||
| e7e4053f60 | |||
| 3428fa968f | |||
| cf50064365 | |||
| d2ffd09079 |
282
ads1298.c
282
ads1298.c
@ -13,15 +13,9 @@
|
|||||||
#include "nrf_gpio.h"
|
#include "nrf_gpio.h"
|
||||||
#include "nrf_drv_gpiote.h"
|
#include "nrf_drv_gpiote.h"
|
||||||
|
|
||||||
#include "base/usb_logging.h"
|
#include "custom_board.h"
|
||||||
|
|
||||||
#include <string.h>
|
#if ADS1298
|
||||||
|
|
||||||
String create_string(char* data) {
|
|
||||||
s32 count = strlen(data);
|
|
||||||
String s = {count, data};
|
|
||||||
return s;
|
|
||||||
} // #MOVE.
|
|
||||||
|
|
||||||
uint8_t ads1298_default_regs[] = {
|
uint8_t ads1298_default_regs[] = {
|
||||||
ADS1298_REGDEFAULT_CONFIG1,
|
ADS1298_REGDEFAULT_CONFIG1,
|
||||||
@ -63,7 +57,7 @@ static void spi_event_handler(nrf_drv_spi_evt_t const *p_event, void *p_context)
|
|||||||
{
|
{
|
||||||
spi_xfer_done = true;
|
spi_xfer_done = true;
|
||||||
#if ADS1298_LOG_DEBUG
|
#if ADS1298_LOG_DEBUG
|
||||||
JIIM_LOG(" > SPI transfer completed.");
|
NRF_LOG_INFO(" > SPI transfer completed.");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,10 +76,12 @@ void ads1298_initialize(ads1298_info_t *p_info) {
|
|||||||
spi_config.miso_pin = ADS1298_MISO_PIN;
|
spi_config.miso_pin = ADS1298_MISO_PIN;
|
||||||
spi_config.mosi_pin = ADS1298_MOSI_PIN;
|
spi_config.mosi_pin = ADS1298_MOSI_PIN;
|
||||||
spi_config.sck_pin = ADS1298_SCK_PIN;
|
spi_config.sck_pin = ADS1298_SCK_PIN;
|
||||||
|
spi_config.frequency = NRF_DRV_SPI_FREQ_250K;
|
||||||
//spi_config.frequency = NRF_DRV_SPI_FREQ_500K;
|
//spi_config.frequency = NRF_DRV_SPI_FREQ_500K;
|
||||||
spi_config.frequency = NRF_DRV_SPI_FREQ_1M;
|
//spi_config.frequency = NRF_DRV_SPI_FREQ_1M;
|
||||||
//spi_config.frequency = NRF_DRV_SPI_FREQ_2M;
|
//spi_config.frequency = NRF_DRV_SPI_FREQ_2M;
|
||||||
spi_config.irq_priority = APP_IRQ_PRIORITY_HIGHEST;
|
spi_config.irq_priority = APP_IRQ_PRIORITY_HIGH;
|
||||||
|
//spi_config.irq_priority = APP_IRQ_PRIORITY_HIGHEST;
|
||||||
spi_config.mode = NRF_DRV_SPI_MODE_1;
|
spi_config.mode = NRF_DRV_SPI_MODE_1;
|
||||||
spi_config.orc = 0x55;
|
spi_config.orc = 0x55;
|
||||||
|
|
||||||
@ -115,34 +111,56 @@ void ads1298_initialize(ads1298_info_t *p_info) {
|
|||||||
#if ADS1298_LOG_DEBUG
|
#if ADS1298_LOG_DEBUG
|
||||||
NRF_LOG_FLUSH();
|
NRF_LOG_FLUSH();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// 0xDF = see extended table
|
||||||
|
p_info->usb_buffer[0] = IC_ID_ADS1298;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ads1298_uninitialize(void) {
|
void ads1298_uninitialize(void) {
|
||||||
JIIM_LOG("Uninitializing ADS1298...");
|
NRF_LOG_INFO("Uninitializing ADS1298...");
|
||||||
nrf_drv_spi_uninit(&spi0);
|
nrf_drv_spi_uninit(&spi0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ads1298_readback_registers(ads1298_info_t* p_info) {
|
||||||
|
// Copies into USB Buffer:
|
||||||
|
uint8_t registers_to_read = ADS1298_REGISTER_COUNT;
|
||||||
|
uint8_t tx_buf[ADS1298_REGISTER_COUNT + 2] = {0};
|
||||||
|
tx_buf[0] = ADS1298_OPC_RREG|ADS1298_REGADDR_ID;
|
||||||
|
tx_buf[1] = registers_to_read-1;
|
||||||
|
uint8_t rx_buf[ADS1298_REGISTER_COUNT + 2] = {0};
|
||||||
|
spi_xfer_done = false;
|
||||||
|
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi0, tx_buf, ADS1298_REGISTER_COUNT + 2, rx_buf, ADS1298_REGISTER_COUNT + 2));
|
||||||
|
while(!spi_xfer_done) { __WFE(); }
|
||||||
|
|
||||||
|
memcpy(p_info->registers, &rx_buf[2], ADS1298_REGISTER_COUNT);
|
||||||
|
NRF_LOG_INFO("Reading ADS1298 registers:");
|
||||||
|
NRF_LOG_HEXDUMP_INFO(&rx_buf[2], ADS1298_REGISTER_COUNT);
|
||||||
|
}
|
||||||
|
|
||||||
bool ads1298_check_id(ads1298_info_t *p_info) {
|
bool ads1298_check_id(ads1298_info_t *p_info) {
|
||||||
bool device_found = false;
|
bool device_found = false;
|
||||||
#if ADS1298_LOG_DEBUG
|
#if ADS1298_LOG_DEBUG
|
||||||
JIIM_LOG("Checking ADS129xR? ID:");
|
NRF_LOG_INFO("Checking ADS129xR? ID:");
|
||||||
#endif
|
#endif
|
||||||
uint8_t tx_buf[6] = {ADS1298_OPC_RREG|ADS1298_REGADDR_ID, 0x01, 0, 0, 0, 0};
|
|
||||||
|
uint8_t registers_to_read = 4;
|
||||||
|
// opcode1, registers to read minus 1
|
||||||
|
uint8_t tx_buf[6] = {ADS1298_OPC_RREG|ADS1298_REGADDR_ID, registers_to_read-1, 0, 0, 0, 0};
|
||||||
uint8_t rx_buf[6] = {0,0,0,0,0,0};
|
uint8_t rx_buf[6] = {0,0,0,0,0,0};
|
||||||
spi_xfer_done = false;
|
spi_xfer_done = false;
|
||||||
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi0, tx_buf, 2, rx_buf, 6));
|
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi0, tx_buf, 6, rx_buf, 6));
|
||||||
while(!spi_xfer_done) {
|
while(!spi_xfer_done) { __WFE(); }
|
||||||
__WFE();
|
uint8_t register_data = rx_buf[2];
|
||||||
}
|
memcpy(p_info->id_buffer, rx_buf, 6);
|
||||||
#if ADS1298_LOG_DEBUG
|
#if ADS1298_LOG_DEBUG
|
||||||
JIIM_LOG("rx_buf[3] = 0x%X", rx_buf[3]);
|
NRF_LOG_INFO("register_data[0] = 0x%X", register_data);
|
||||||
JIIM_LOG("rx_buf dump:");
|
NRF_LOG_INFO(">rx_buf dump:");
|
||||||
JIIM_LOG("rx_buf[0:5] = 0x%X%X%X%X%X", rx_buf[0], rx_buf[1], rx_buf[2], rx_buf[3], rx_buf[4], rx_buf[5]);
|
NRF_LOG_INFO(">rx_buf[0:5] = 0x[%X %X %X %X %X]", rx_buf[0], rx_buf[1], rx_buf[2], rx_buf[3], rx_buf[4], rx_buf[5]);
|
||||||
|
|
||||||
// NRF_LOG_HEXDUMP_INFO(rx_buf, 6);
|
// NRF_LOG_HEXDUMP_INFO(rx_buf, 6);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Check lower 3 bits 0x[...]..[xxx]
|
// Check lower 3 bits 0x[...]..[xxx]
|
||||||
uint8_t nch_check = rx_buf[3] & 0x07; // 0x.....111
|
uint8_t nch_check = register_data & 0x07; // 0x.....111
|
||||||
p_info->nChs = 0;
|
p_info->nChs = 0;
|
||||||
if (nch_check == ADS129x_4CH_BITMASK) {
|
if (nch_check == ADS129x_4CH_BITMASK) {
|
||||||
p_info->nChs = 4; // ADS1294
|
p_info->nChs = 4; // ADS1294
|
||||||
@ -151,26 +169,27 @@ bool ads1298_check_id(ads1298_info_t *p_info) {
|
|||||||
} else if (nch_check == ADS129x_8CH_BITMASK) {
|
} else if (nch_check == ADS129x_8CH_BITMASK) {
|
||||||
p_info->nChs = 8; // ADS1298
|
p_info->nChs = 8; // ADS1298
|
||||||
} else {
|
} else {
|
||||||
JIIM_LOG("[ERROR] Expected ADS129xX not detected! E0");
|
NRF_LOG_INFO("[ERROR] Expected ADS129xX not detected! E0");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Check middle bits 0x[...]10[...] < always should be 10
|
// Check middle bits 0x[...]10[...] < always should be 10
|
||||||
if ((rx_buf[3] & 0x10) != 0x10 || p_info->nChs == 0) {
|
if ((register_data & 0x10) != 0x10 || p_info->nChs == 0) {
|
||||||
JIIM_LOG("[ERROR] Expected ADS129xX not detected! E1");
|
p_info->nChs = 8;
|
||||||
|
NRF_LOG_INFO("[ERROR] Expected ADS129xX not detected! E1");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Check first three bits: 0x[xxx]..[...]
|
// Check first three bits: 0x[xxx]..[...]
|
||||||
uint8_t r_ver_chk = rx_buf[3] & 0xE0;
|
uint8_t r_ver_chk = register_data & 0xE0;
|
||||||
if (r_ver_chk == ADS129x_DEVICE_FAMILY_BITMASK) {
|
if (r_ver_chk == ADS129x_DEVICE_FAMILY_BITMASK) {
|
||||||
p_info->name_len = sprintf(p_info->name, "ADS129%d", p_info->nChs);
|
p_info->name_len = sprintf(p_info->name, "ADS129%d", p_info->nChs);
|
||||||
JIIM_LOG("%s detected! (name_len: %d)", p_info->name, p_info->name_len);
|
NRF_LOG_INFO("%s detected! (name_len: %d)", p_info->name, p_info->name_len);
|
||||||
device_found = true;
|
device_found = true;
|
||||||
} else if (r_ver_chk == ADS129xR_DEVICE_FAMILY_BITMASK) {
|
} else if (r_ver_chk == ADS129xR_DEVICE_FAMILY_BITMASK) {
|
||||||
p_info->name_len = sprintf(p_info->name, "ADS129%dR", p_info->nChs);
|
p_info->name_len = sprintf(p_info->name, "ADS129%dR", p_info->nChs);
|
||||||
JIIM_LOG("%s detected! (name_len: %d)", p_info->name, p_info->name_len);
|
NRF_LOG_INFO("%s detected! (name_len: %d)", p_info->name, p_info->name_len);
|
||||||
device_found = true;
|
device_found = true;
|
||||||
} else {
|
} else {
|
||||||
JIIM_LOG("[ERROR] Expected ADS129xX not detected! E2");
|
NRF_LOG_INFO("[ERROR] Expected ADS129xX not detected! E2");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,7 +200,7 @@ bool ads1298_check_id(ads1298_info_t *p_info) {
|
|||||||
__STATIC_INLINE bool ads1298_check_channel(ads1298_info_t *p_info, uint8_t chNumber) {
|
__STATIC_INLINE bool ads1298_check_channel(ads1298_info_t *p_info, uint8_t chNumber) {
|
||||||
if (chNumber > p_info->nChs) return false;
|
if (chNumber > p_info->nChs) return false;
|
||||||
if (chNumber > 8) {
|
if (chNumber > 8) {
|
||||||
JIIM_LOG("[ads1298_check_channel] INVALID CHANNEL NUMBER: %d!", chNumber);
|
NRF_LOG_INFO("[ads1298_check_channel] INVALID CHANNEL NUMBER: %d!", chNumber);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return !((p_info->registers[3 + chNumber] & 0x80) == 0x80);
|
return !((p_info->registers[3 + chNumber] & 0x80) == 0x80);
|
||||||
@ -201,8 +220,31 @@ void ads1298_update_active_chs(ads1298_info_t *p_info) {
|
|||||||
if (ads1298_check_channel(p_info, 8)) p_info->active_chs |= 0x01;
|
if (ads1298_check_channel(p_info, 8)) p_info->active_chs |= 0x01;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const uint8_t popcount_table[256] = {
|
||||||
|
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
|
||||||
|
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
||||||
|
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
||||||
|
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||||
|
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
||||||
|
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||||
|
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||||
|
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
||||||
|
1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
|
||||||
|
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||||
|
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||||
|
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
||||||
|
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
|
||||||
|
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
||||||
|
3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
|
||||||
|
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline uint8_t ads1298_channel_count(ads1298_info_t *p_info) {
|
||||||
|
return popcount_table[p_info->active_chs];
|
||||||
|
}
|
||||||
|
|
||||||
void ads1298_update_registers(ads1298_info_t *p_info) {
|
void ads1298_update_registers(ads1298_info_t *p_info) {
|
||||||
JIIM_LOG("Updating ADS1298 registers...");
|
NRF_LOG_INFO("Updating ADS1298 registers...");
|
||||||
ads1298_update_active_chs(p_info);
|
ads1298_update_active_chs(p_info);
|
||||||
uint8_t i = 0; // Register index
|
uint8_t i = 0; // Register index
|
||||||
uint8_t tx_data[ADS1298_REGISTER_COUNT + 2]; // plus 2 for opcodes
|
uint8_t tx_data[ADS1298_REGISTER_COUNT + 2]; // plus 2 for opcodes
|
||||||
@ -217,17 +259,16 @@ void ads1298_update_registers(ads1298_info_t *p_info) {
|
|||||||
// Transaction size = 2 + number of registers to write
|
// Transaction size = 2 + number of registers to write
|
||||||
spi_xfer_done = false;
|
spi_xfer_done = false;
|
||||||
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi0, tx_data, ADS1298_REGISTER_COUNT + 2, rx_data, ADS1298_REGISTER_COUNT + 2));
|
APP_ERROR_CHECK(nrf_drv_spi_transfer(&spi0, tx_data, ADS1298_REGISTER_COUNT + 2, rx_data, ADS1298_REGISTER_COUNT + 2));
|
||||||
while (!spi_xfer_done)
|
while (!spi_xfer_done) { __WFE(); }
|
||||||
{
|
|
||||||
__WFE();
|
|
||||||
}
|
|
||||||
#if ADS1298_LOG_DEBUG
|
#if ADS1298_LOG_DEBUG
|
||||||
JIIM_LOG("[ADS1298] Registers updated!");
|
NRF_LOG_INFO("[ADS1298] Registers updated!");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// #TODO: Readback registers?
|
||||||
}
|
}
|
||||||
|
|
||||||
void ads1298_init_default_registers(void) {
|
void ads1298_init_default_registers(void) {
|
||||||
JIIM_LOG("Initializing ADS1298 registers...");
|
NRF_LOG_INFO("Initializing ADS1298 registers...");
|
||||||
uint8_t i = 0; // Register index
|
uint8_t i = 0; // Register index
|
||||||
uint8_t tx_data[ADS1298_REGISTER_COUNT + 2]; // plus 2 for opcodes
|
uint8_t tx_data[ADS1298_REGISTER_COUNT + 2]; // plus 2 for opcodes
|
||||||
uint8_t rx_data[ADS1298_REGISTER_COUNT + 2];
|
uint8_t rx_data[ADS1298_REGISTER_COUNT + 2];
|
||||||
@ -246,7 +287,7 @@ void ads1298_init_default_registers(void) {
|
|||||||
__WFE();
|
__WFE();
|
||||||
}
|
}
|
||||||
#if ADS1298_LOG_DEBUG
|
#if ADS1298_LOG_DEBUG
|
||||||
JIIM_LOG("[ADS1298] Registers initialized.");
|
NRF_LOG_INFO("[ADS1298] Registers initialized.");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,13 +298,14 @@ void ads1298_power_down(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ads1298_power_up(void) {
|
void ads1298_power_up(void) {
|
||||||
|
nrf_delay_ms(100); // wait for power supplies to stabilize
|
||||||
nrf_gpio_pin_set(ADS1298_PWDN_PIN);
|
nrf_gpio_pin_set(ADS1298_PWDN_PIN);
|
||||||
nrf_delay_ms(80);
|
nrf_delay_ms(100);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Standby/Wakeup controls:
|
// Standby/Wakeup controls:
|
||||||
void ads1298_wakeup(void) {
|
void ads1298_wakeup(void) {
|
||||||
JIIM_LOG("Waking up ADS1298...");
|
NRF_LOG_INFO("Waking up ADS1298...");
|
||||||
uint8_t cmd = ADS1298_OPC_WAKEUP;
|
uint8_t cmd = ADS1298_OPC_WAKEUP;
|
||||||
uint8_t rx_buf;
|
uint8_t rx_buf;
|
||||||
spi_xfer_done = false;
|
spi_xfer_done = false;
|
||||||
@ -275,7 +317,7 @@ void ads1298_wakeup(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ads1298_standby(void) {
|
void ads1298_standby(void) {
|
||||||
JIIM_LOG("Placing ADS1298 in standby mode...");
|
NRF_LOG_INFO("Placing ADS1298 in standby mode...");
|
||||||
uint8_t cmd = ADS1298_OPC_STANDBY;
|
uint8_t cmd = ADS1298_OPC_STANDBY;
|
||||||
uint8_t rx_buf;
|
uint8_t rx_buf;
|
||||||
spi_xfer_done = false;
|
spi_xfer_done = false;
|
||||||
@ -288,7 +330,7 @@ void ads1298_standby(void) {
|
|||||||
|
|
||||||
// Start/stop conversions
|
// Start/stop conversions
|
||||||
void ads1298_soft_start_conversion(void) {
|
void ads1298_soft_start_conversion(void) {
|
||||||
JIIM_LOG("[ADS1298] Starting conversion...");
|
NRF_LOG_INFO("[ADS1298] Starting conversion...");
|
||||||
uint8_t cmd = ADS1298_OPC_START;
|
uint8_t cmd = ADS1298_OPC_START;
|
||||||
uint8_t rx_buf;
|
uint8_t rx_buf;
|
||||||
spi_xfer_done = false;
|
spi_xfer_done = false;
|
||||||
@ -300,7 +342,7 @@ void ads1298_soft_start_conversion(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ads1298_start_rdatac(void) {
|
void ads1298_start_rdatac(void) {
|
||||||
JIIM_LOG("[ADS1298] Starting Read Data Continuous Mode...");
|
NRF_LOG_INFO("[ADS1298] Starting Read Data Continuous Mode...");
|
||||||
uint8_t cmd = ADS1298_OPC_RDATAC;
|
uint8_t cmd = ADS1298_OPC_RDATAC;
|
||||||
uint8_t rx_buf;
|
uint8_t rx_buf;
|
||||||
spi_xfer_done = false;
|
spi_xfer_done = false;
|
||||||
@ -312,7 +354,7 @@ void ads1298_start_rdatac(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ads1298_stop_rdatac(void) {
|
void ads1298_stop_rdatac(void) {
|
||||||
JIIM_LOG("[ADS1298] Stopping Read Data Continuous Mode...");
|
NRF_LOG_INFO("[ADS1298] Stopping Read Data Continuous Mode...");
|
||||||
uint8_t cmd = ADS1298_OPC_SDATAC;
|
uint8_t cmd = ADS1298_OPC_SDATAC;
|
||||||
uint8_t rx_buf;
|
uint8_t rx_buf;
|
||||||
spi_xfer_done = false;
|
spi_xfer_done = false;
|
||||||
@ -334,43 +376,153 @@ void ads1298_stop_rdatac(void) {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// TODO: Add option for 2 bytes (low resolution)
|
uint16_t ads1298_sampling_rate(ads1298_info_t* p_info) {
|
||||||
/*
|
// Check if we're in high precision or low power mode.
|
||||||
__STATIC_INLINE void copy_relevant_data(ble_exg_t *p_exg, uint8_t active_chs) {
|
// We default to high precision. If LP, just div2.
|
||||||
if ((active_chs & 0x80) == 0x80) memcpy(&p_exg->exg_ch1_buffer[p_exg->data_buffer_count], &rx_buffer[3], 3);
|
uint16_t sampling_rate;
|
||||||
if ((active_chs & 0x40) == 0x40) memcpy(&p_exg->exg_ch2_buffer[p_exg->data_buffer_count], &rx_buffer[6], 3);
|
switch (p_info->registers[0] & 0x07) {
|
||||||
if ((active_chs & 0x20) == 0x20) memcpy(&p_exg->exg_ch3_buffer[p_exg->data_buffer_count], &rx_buffer[9], 3);
|
case 0:
|
||||||
if ((active_chs & 0x10) == 0x10) memcpy(&p_exg->exg_ch4_buffer[p_exg->data_buffer_count], &rx_buffer[12], 3);
|
sampling_rate = 32000;
|
||||||
if ((active_chs & 0x08) == 0x08) memcpy(&p_exg->exg_ch5_buffer[p_exg->data_buffer_count], &rx_buffer[15], 3);
|
case 1:
|
||||||
if ((active_chs & 0x04) == 0x04) memcpy(&p_exg->exg_ch6_buffer[p_exg->data_buffer_count], &rx_buffer[18], 3);
|
sampling_rate = 16000;
|
||||||
if ((active_chs & 0x02) == 0x02) memcpy(&p_exg->exg_ch7_buffer[p_exg->data_buffer_count], &rx_buffer[21], 3);
|
case 2:
|
||||||
if ((active_chs & 0x01) == 0x01) memcpy(&p_exg->exg_ch8_buffer[p_exg->data_buffer_count], &rx_buffer[24], 3);
|
sampling_rate = 8000;
|
||||||
p_exg->data_buffer_count += 3;
|
case 3:
|
||||||
|
sampling_rate = 4000;
|
||||||
|
case 4:
|
||||||
|
sampling_rate = 2000;
|
||||||
|
case 5:
|
||||||
|
sampling_rate = 1000;
|
||||||
|
case 6:
|
||||||
|
sampling_rate = 500;
|
||||||
|
default:
|
||||||
|
sampling_rate = 500;
|
||||||
|
}
|
||||||
|
if (p_info->registers[0] & 0x80) {
|
||||||
|
sampling_rate /= sampling_rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sampling_rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Buffer size depends on version (27 bytes for ADS1298, 21 for ADS1296, and 15 for ADS1294)
|
void ads1298_set_data_buffer_length(ads1298_info_t* p_info) {
|
||||||
void ads1298_get_data(ble_exg_t *p_exg, uint8_t active_chs) {
|
uint16_t sampling_rate = ads1298_sampling_rate(p_info);
|
||||||
|
// Will depend not on sampling rate but on number of active channels
|
||||||
|
// #NOTE: Will need to update registers first.
|
||||||
|
uint8_t channel_count = ads1298_channel_count(p_info);
|
||||||
|
switch (channel_count) {
|
||||||
|
case 1: // Recall the max count is 64
|
||||||
|
p_info->usb_buffer_size_max = (3 * 20) + ADS1298_PACKET_OFFSET;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
p_info->usb_buffer_size_max = (3 * 20) + ADS1298_PACKET_OFFSET;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
p_info->usb_buffer_size_max = (3 * 18) + ADS1298_PACKET_OFFSET;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
p_info->usb_buffer_size_max = (3 * 20) + ADS1298_PACKET_OFFSET;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
p_info->usb_buffer_size_max = (3 * 20) + ADS1298_PACKET_OFFSET;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
p_info->usb_buffer_size_max = (3 * 18) + ADS1298_PACKET_OFFSET;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
p_info->usb_buffer_size_max = (3 * 14) + ADS1298_PACKET_OFFSET;
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
p_info->usb_buffer_size_max = (3 * 16) + ADS1298_PACKET_OFFSET;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
p_info->usb_buffer_size_max = (3 * 16) + ADS1298_PACKET_OFFSET;
|
||||||
|
// This should never happen!
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
NRF_LOG_INFO("[ADS129x] Sampling rate is %d", sampling_rate);
|
||||||
|
NRF_LOG_INFO("[ADS129x] p_info->nChs is %d", p_info->nChs);
|
||||||
|
NRF_LOG_INFO("[ADS129x] Channel count is %d, active_channels: 0x%X", channel_count, p_info->active_chs);
|
||||||
|
NRF_LOG_INFO("[ADS129x] p_info->usb_buffer_size_max is %d", p_info->usb_buffer_size_max);
|
||||||
|
}
|
||||||
|
|
||||||
|
__STATIC_INLINE void copy_relevant_data(ads1298_info_t* p_info) {
|
||||||
|
if ((p_info->active_chs & 0x80) == 0x80) {
|
||||||
|
memcpy(&p_info->usb_buffer[p_info->usb_buffer_count], &rx_buffer[3], 3);
|
||||||
|
p_info->usb_buffer_count += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((p_info->active_chs & 0x40) == 0x40) {
|
||||||
|
memcpy(&p_info->usb_buffer[p_info->usb_buffer_count], &rx_buffer[6], 3);
|
||||||
|
p_info->usb_buffer_count += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((p_info->active_chs & 0x20) == 0x20) {
|
||||||
|
memcpy(&p_info->usb_buffer[p_info->usb_buffer_count], &rx_buffer[9], 3);
|
||||||
|
p_info->usb_buffer_count += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((p_info->active_chs & 0x10) == 0x10) {
|
||||||
|
memcpy(&p_info->usb_buffer[p_info->usb_buffer_count], &rx_buffer[12], 3);
|
||||||
|
p_info->usb_buffer_count += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((p_info->active_chs & 0x08) == 0x08) {
|
||||||
|
memcpy(&p_info->usb_buffer[p_info->usb_buffer_count], &rx_buffer[15], 3);
|
||||||
|
p_info->usb_buffer_count += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((p_info->active_chs & 0x04) == 0x04) {
|
||||||
|
memcpy(&p_info->usb_buffer[p_info->usb_buffer_count], &rx_buffer[18], 3);
|
||||||
|
p_info->usb_buffer_count += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((p_info->active_chs & 0x02) == 0x02) {
|
||||||
|
memcpy(&p_info->usb_buffer[p_info->usb_buffer_count], &rx_buffer[21], 3);
|
||||||
|
p_info->usb_buffer_count += 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((p_info->active_chs & 0x01) == 0x01) {
|
||||||
|
memcpy(&p_info->usb_buffer[p_info->usb_buffer_count], &rx_buffer[24], 3);
|
||||||
|
p_info->usb_buffer_count += 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ads1298_get_data_fast(ads1298_info_t* p_info) {
|
||||||
spi_xfer_done = false;
|
spi_xfer_done = false;
|
||||||
|
// necessary?
|
||||||
memset(rx_buffer, 0, 27);
|
memset(rx_buffer, 0, 27);
|
||||||
nrf_drv_spi_transfer(&spi0, rx_buffer, 27, rx_buffer, 27);
|
nrf_drv_spi_transfer(&spi0, rx_buffer, 27, rx_buffer, 27);
|
||||||
while (!spi_xfer_done) { __WFE(); }
|
while (!spi_xfer_done) { __WFE(); }
|
||||||
copy_relevant_data(p_exg, active_chs);
|
memcpy(&p_info->usb_buffer[p_info->usb_buffer_count], &rx_buffer[3], 8 * 3);
|
||||||
|
p_info->usb_buffer_count += 3 * 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ads1296_get_data(ble_exg_t *p_exg, uint8_t active_chs) {
|
// Buffer size depends on version (27 bytes for ADS1298, 21 for ADS1296, and 15 for ADS1294)
|
||||||
|
void ads1298_get_data(ads1298_info_t* p_info) {
|
||||||
|
spi_xfer_done = false;
|
||||||
|
// necessary?
|
||||||
|
memset(rx_buffer, 0, 27);
|
||||||
|
nrf_drv_spi_transfer(&spi0, rx_buffer, 27, rx_buffer, 27);
|
||||||
|
while (!spi_xfer_done) { __WFE(); }
|
||||||
|
copy_relevant_data(p_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ads1296_get_data(ads1298_info_t* p_info) {
|
||||||
spi_xfer_done = false;
|
spi_xfer_done = false;
|
||||||
memset(rx_buffer, 0, 21);
|
memset(rx_buffer, 0, 21);
|
||||||
nrf_drv_spi_transfer(&spi0, rx_buffer, 21, rx_buffer, 21);
|
nrf_drv_spi_transfer(&spi0, rx_buffer, 21, rx_buffer, 21);
|
||||||
while (!spi_xfer_done) { __WFE(); }
|
while (!spi_xfer_done) { __WFE(); }
|
||||||
copy_relevant_data(p_exg, active_chs);
|
copy_relevant_data(p_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ads1294_get_data(ble_exg_t *p_exg, uint8_t active_chs) {
|
void ads1294_get_data(ads1298_info_t* p_info) {
|
||||||
spi_xfer_done = false;
|
spi_xfer_done = false;
|
||||||
memset(rx_buffer, 0, 15);
|
memset(rx_buffer, 0, 15);
|
||||||
nrf_drv_spi_transfer(&spi0, rx_buffer, 15, rx_buffer, 15);
|
nrf_drv_spi_transfer(&spi0, rx_buffer, 15, rx_buffer, 15);
|
||||||
while (!spi_xfer_done) { __WFE(); }
|
while (!spi_xfer_done) { __WFE(); }
|
||||||
// Check mode p_exg->ads1298_settings[26] b0
|
// Check mode p_exg->ads1298_settings[26] b0
|
||||||
copy_relevant_data(p_exg, active_chs);
|
copy_relevant_data(p_info);
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
#endif // ADS1298
|
||||||
65
ads1298.h
65
ads1298.h
@ -4,19 +4,14 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "nrf_drv_spi.h"
|
#include "nrf_drv_spi.h"
|
||||||
|
|
||||||
// Debug flag for logging:
|
#include "custom_board.h"
|
||||||
#define ADS1298_LOG_DEBUG 1
|
|
||||||
|
|
||||||
// Testing with nRF52840-DK:
|
// Debug flag for logging:
|
||||||
#define ADS1298_DRDY_PIN 11
|
#define ADS1298_LOG_DEBUG 0
|
||||||
#define ADS1298_MISO_PIN 12
|
|
||||||
#define ADS1298_SCK_PIN 13
|
|
||||||
#define ADS1298_CS_PIN 14
|
|
||||||
#define ADS1298_PWDN_PIN 15
|
|
||||||
#define ADS1298_MOSI_PIN 16
|
|
||||||
|
|
||||||
// Number of WRITABLE registers (Not inc. ID register)
|
// Number of WRITABLE registers (Not inc. ID register)
|
||||||
#define ADS1298_REGISTER_COUNT 25
|
#define ADS1298_REGISTER_COUNT 25
|
||||||
|
#define ADS1298_PACKET_OFFSET 2 // [device id][serial id][..data]
|
||||||
|
|
||||||
/** REGISTER ADDRESSES **/
|
/** REGISTER ADDRESSES **/
|
||||||
#define ADS1298_REGADDR_ID 0x00
|
#define ADS1298_REGADDR_ID 0x00
|
||||||
@ -70,19 +65,19 @@
|
|||||||
#define ADS129x_8CH_BITMASK 0x02 // 0x[...]10[010]
|
#define ADS129x_8CH_BITMASK 0x02 // 0x[...]10[010]
|
||||||
|
|
||||||
/** DEFAULT REGISTER VALUES **/
|
/** DEFAULT REGISTER VALUES **/
|
||||||
//#define ADS1298_REGDEFAULT_CONFIG1 0x05 // Low power mode, Daisy-chain mode, clk output disabled, LP: 250 SPS
|
//
|
||||||
#define ADS1298_REGDEFAULT_CONFIG1 0x46 // Low power mode, Multiple readback mode, clk output disabled, LP: 250 SPS
|
#define ADS1298_REGDEFAULT_CONFIG1 0xC5 // High-res mode, Multiple readback mode, clk output disabled, LP: 250 SPS
|
||||||
#define ADS1298_REGDEFAULT_CONFIG2 0x00 // Test signals
|
#define ADS1298_REGDEFAULT_CONFIG2 0x00 // Test signals
|
||||||
#define ADS1298_REGDEFAULT_CONFIG3 0x40 //
|
#define ADS1298_REGDEFAULT_CONFIG3 0xCE //
|
||||||
#define ADS1298_REGDEFAULT_LOFF 0x00
|
#define ADS1298_REGDEFAULT_LOFF 0x00
|
||||||
#define ADS1298_REGDEFAULT_CH1SET 0x00
|
#define ADS1298_REGDEFAULT_CH1SET 0x81 // Input Short (for startup)
|
||||||
#define ADS1298_REGDEFAULT_CH2SET 0x00
|
#define ADS1298_REGDEFAULT_CH2SET 0x81 // Input Short (for startup)
|
||||||
#define ADS1298_REGDEFAULT_CH3SET 0x00
|
#define ADS1298_REGDEFAULT_CH3SET 0x81 // Input Short (for startup)
|
||||||
#define ADS1298_REGDEFAULT_CH4SET 0x00
|
#define ADS1298_REGDEFAULT_CH4SET 0x81 // Input Short (for startup)
|
||||||
#define ADS1298_REGDEFAULT_CH5SET 0x00
|
#define ADS1298_REGDEFAULT_CH5SET 0x81 // Input Short (for startup)
|
||||||
#define ADS1298_REGDEFAULT_CH6SET 0x00
|
#define ADS1298_REGDEFAULT_CH6SET 0x81 // Input Short (for startup)
|
||||||
#define ADS1298_REGDEFAULT_CH7SET 0x00
|
#define ADS1298_REGDEFAULT_CH7SET 0x81 // Input Short (for startup)
|
||||||
#define ADS1298_REGDEFAULT_CH8SET 0x00
|
#define ADS1298_REGDEFAULT_CH8SET 0x81 // Input Short (for startup)
|
||||||
#define ADS1298_REGDEFAULT_RLD_SENSP 0x00
|
#define ADS1298_REGDEFAULT_RLD_SENSP 0x00
|
||||||
#define ADS1298_REGDEFAULT_RLD_SENSN 0x00
|
#define ADS1298_REGDEFAULT_RLD_SENSN 0x00
|
||||||
#define ADS1298_REGDEFAULT_LOFF_SENSP 0x00
|
#define ADS1298_REGDEFAULT_LOFF_SENSP 0x00
|
||||||
@ -100,6 +95,8 @@
|
|||||||
#define ADS1298_BUFFER_SIZE 64
|
#define ADS1298_BUFFER_SIZE 64
|
||||||
#define ADS1298_SETTINGS_SIZE 26
|
#define ADS1298_SETTINGS_SIZE 26
|
||||||
|
|
||||||
|
#define USBD_MAX_SIZE 64
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t nChs; // 4, 6, or 8 channels depending on variant.
|
uint8_t nChs; // 4, 6, or 8 channels depending on variant.
|
||||||
uint8_t state; // Powered on or off!
|
uint8_t state; // Powered on or off!
|
||||||
@ -109,19 +106,30 @@ typedef struct {
|
|||||||
uint8_t active_chs;
|
uint8_t active_chs;
|
||||||
uint8_t registers[ADS1298_REGISTER_COUNT];
|
uint8_t registers[ADS1298_REGISTER_COUNT];
|
||||||
char name[12];
|
char name[12];
|
||||||
|
uint8_t id_buffer[6];
|
||||||
|
char usb_buffer[USBD_MAX_SIZE];
|
||||||
|
uint8_t usb_buffer_count;
|
||||||
|
uint8_t usb_buffer_size_max;
|
||||||
|
#if ADS1298_STATS
|
||||||
|
uint32_t drdy_trigger_count;
|
||||||
|
uint32_t seconds_elapsed;
|
||||||
|
uint32_t bytes_sent_usb;
|
||||||
|
#endif
|
||||||
} ads1298_info_t;
|
} ads1298_info_t;
|
||||||
|
|
||||||
/** FUNCTION PROTOTYPES **/
|
/** FUNCTION PROTOTYPES **/
|
||||||
void ads1298_initialize(ads1298_info_t *p_info);
|
void ads1298_initialize(ads1298_info_t* p_info);
|
||||||
|
|
||||||
void ads1298_uninitialize(void);
|
void ads1298_uninitialize(void);
|
||||||
|
|
||||||
bool ads1298_check_id(ads1298_info_t *p_info);
|
bool ads1298_check_id(ads1298_info_t* p_info);
|
||||||
|
|
||||||
void ads1298_update_registers(ads1298_info_t *p_info);
|
void ads1298_update_registers(ads1298_info_t* p_info);
|
||||||
|
|
||||||
void ads1298_init_default_registers(void);
|
void ads1298_init_default_registers(void);
|
||||||
|
|
||||||
|
void ads1298_readback_registers(ads1298_info_t* p_info);
|
||||||
|
|
||||||
void ads1298_power_down(void);
|
void ads1298_power_down(void);
|
||||||
|
|
||||||
void ads1298_power_up(void);
|
void ads1298_power_up(void);
|
||||||
@ -136,10 +144,15 @@ void ads1298_start_rdatac(void);
|
|||||||
|
|
||||||
void ads1298_stop_rdatac(void);
|
void ads1298_stop_rdatac(void);
|
||||||
|
|
||||||
// void ads1294_get_data(ble_exg_t *p_exg, uint8_t active_chs);
|
void ads1294_get_data(ads1298_info_t* p_info);
|
||||||
|
|
||||||
// void ads1296_get_data(ble_exg_t *p_exg, uint8_t active_chs);
|
void ads1296_get_data(ads1298_info_t* p_info);
|
||||||
|
|
||||||
// void ads1298_get_data(ble_exg_t *p_exg, uint8_t active_chs);
|
void ads1298_get_data(ads1298_info_t* p_info);
|
||||||
|
void ads1298_get_data_fast(ads1298_info_t* p_info);
|
||||||
|
|
||||||
|
uint16_t ads1298_sampling_rate(ads1298_info_t* p_info);
|
||||||
|
|
||||||
|
void ads1298_set_data_buffer_length(ads1298_info_t* p_info);
|
||||||
|
|
||||||
#endif // ADS1298_H_
|
#endif // ADS1298_H_
|
||||||
|
|||||||
32
base/types.h
32
base/types.h
@ -1,32 +0,0 @@
|
|||||||
#ifndef __TYPES_H
|
|
||||||
#define __TYPES_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
typedef uint8_t u8;
|
|
||||||
typedef uint16_t u16;
|
|
||||||
typedef uint32_t u32;
|
|
||||||
typedef uint64_t u64;
|
|
||||||
typedef int8_t s8;
|
|
||||||
typedef int16_t s16;
|
|
||||||
typedef int32_t s32;
|
|
||||||
typedef int64_t s64;
|
|
||||||
typedef s8 b8;
|
|
||||||
typedef s16 b16;
|
|
||||||
typedef s32 b32;
|
|
||||||
typedef float f32;
|
|
||||||
|
|
||||||
#define Create_String(s) ((String){.count=(sizeof(s)-1), .data=(s)})
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
s32 count;
|
|
||||||
u8* data;
|
|
||||||
} String;
|
|
||||||
|
|
||||||
String create_string(char* data);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@ -1,52 +0,0 @@
|
|||||||
#ifndef USB_LOGGING_H_
|
|
||||||
#define USB_LOGGING_H_
|
|
||||||
|
|
||||||
// Helper code for saving data to USB
|
|
||||||
#include "base/types.h"
|
|
||||||
|
|
||||||
// Global log buffer and usage:
|
|
||||||
static u8 log_buffer[4096];
|
|
||||||
static s32 m_log_usage = 0;
|
|
||||||
// Sending logs
|
|
||||||
static bool m_send_logs = 0;
|
|
||||||
static s32 m_log_buffer_send_index = 0;
|
|
||||||
static volatile bool tx_complete = true;
|
|
||||||
static bool full_tx_complete = false;
|
|
||||||
|
|
||||||
// Functions for adding to and resetting buffer:
|
|
||||||
#define LOG_MODE 0
|
|
||||||
// Mode 0: do not wrap around, do not append to buffer
|
|
||||||
// Mode 1: wrap around to beginning.
|
|
||||||
|
|
||||||
// FOR STRING LITERALS ONLY
|
|
||||||
// #define JIIM_LOG_USB(strlit) add_to_log_buffer(Create_String(strlit))
|
|
||||||
|
|
||||||
// Dynamic strings
|
|
||||||
static u8 sprint_buffer[128];
|
|
||||||
static s32 sprint_length;
|
|
||||||
|
|
||||||
// #TODO: I need to append a \n here
|
|
||||||
#define JIIM_LOG(fmt, ...) \
|
|
||||||
sprint_length = sprintf(sprint_buffer, fmt, ##__VA_ARGS__); \
|
|
||||||
add_format_string_to_log_buffer()
|
|
||||||
|
|
||||||
void reset_log_buffer(void);
|
|
||||||
void add_to_log_buffer(String str);
|
|
||||||
void add_format_string_to_log_buffer(void);
|
|
||||||
/*
|
|
||||||
void send_usb_log(String str) {
|
|
||||||
// Zero buffer:
|
|
||||||
//memset(str.data, 0, NRF_DRV_USBD_EPSIZE);
|
|
||||||
// Copy string:
|
|
||||||
//memcpy(m_tx_buffer, str.data, str.count);
|
|
||||||
|
|
||||||
app_usbd_cdc_acm_write(&m_app_cdc_acm, str.data, str.count);
|
|
||||||
}
|
|
||||||
|
|
||||||
void log_usb(char* data) {
|
|
||||||
send_usb_log(create_string(data));
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#endif // USB_LOGGING_H_
|
|
||||||
425
main.c
425
main.c
@ -1,5 +1,7 @@
|
|||||||
// Nordic drivers and application code:
|
#include <stdint.h>
|
||||||
// Note: see throughput testing in musa_usbd_cdc_acm
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "nrf.h"
|
#include "nrf.h"
|
||||||
#include "nrf_drv_usbd.h"
|
#include "nrf_drv_usbd.h"
|
||||||
@ -22,34 +24,35 @@
|
|||||||
#include "nrf_log_ctrl.h"
|
#include "nrf_log_ctrl.h"
|
||||||
#include "nrf_log_default_backends.h"
|
#include "nrf_log_default_backends.h"
|
||||||
|
|
||||||
// **** jiim base code ****
|
#include "custom_board.h"
|
||||||
#include "base/types.h"
|
|
||||||
#include "base/usb_logging.h"
|
|
||||||
|
|
||||||
// **** Sensor driver APIs ****
|
static enum recording_mode_t recording_mode = RECORDING_MODE_DISABLED;
|
||||||
#define ADS1298 0
|
|
||||||
#define ADS1292 0
|
|
||||||
|
|
||||||
#if ADS1298
|
#if ADS1298
|
||||||
#include "ads1298.h"
|
#include "ads1298.h"
|
||||||
#include "nrf_gpio.h"
|
|
||||||
#include "nrf_drv_gpiote.h"
|
#include "nrf_drv_gpiote.h"
|
||||||
|
|
||||||
|
static bool drdy_flag = false;
|
||||||
|
ads1298_info_t m_info;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if ADS1292
|
// #TEMP: This should be generated dynamically maybe?
|
||||||
#include "ads1292.c"
|
char g_Peripheral_Info[] = "ADS1298|";
|
||||||
#endif
|
char g_WhoAmI[] = "multimodal_cv_dev0_ecg";
|
||||||
|
uint8_t g_HWFW_Ver[] = {1,0,0,1}; // HW Maj, HW Min, FW Maj, FW Min
|
||||||
|
|
||||||
|
static volatile bool run_throughput_test = false;
|
||||||
|
static char usb_send_buffer[NRF_DRV_USBD_EPSIZE * 2]; // * 2 is just for safety margin in case we overrun!
|
||||||
|
static uint8_t usb_send_buffer_offset = 0;
|
||||||
|
|
||||||
|
#define READ_SIZE 64
|
||||||
|
static char usb_read_buffer[READ_SIZE];
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Enable power USB detection
|
|
||||||
*
|
|
||||||
* Configure if example supports USB port connection
|
|
||||||
*/
|
|
||||||
#ifndef USBD_POWER_DETECTION
|
#ifndef USBD_POWER_DETECTION
|
||||||
#define USBD_POWER_DETECTION true
|
#define USBD_POWER_DETECTION true
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static void cdc_acm_user_ev_handler(app_usbd_class_inst_t const * p_inst,
|
static void cdc_acm_user_ev_handler(app_usbd_class_inst_t const * p_inst,
|
||||||
app_usbd_cdc_acm_user_event_t event);
|
app_usbd_cdc_acm_user_event_t event);
|
||||||
|
|
||||||
@ -61,9 +64,6 @@ static void cdc_acm_user_ev_handler(app_usbd_class_inst_t const * p_inst,
|
|||||||
#define CDC_ACM_DATA_EPOUT NRF_DRV_USBD_EPOUT1
|
#define CDC_ACM_DATA_EPOUT NRF_DRV_USBD_EPOUT1
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief CDC_ACM class instance
|
|
||||||
* */
|
|
||||||
APP_USBD_CDC_ACM_GLOBAL_DEF(m_app_cdc_acm,
|
APP_USBD_CDC_ACM_GLOBAL_DEF(m_app_cdc_acm,
|
||||||
cdc_acm_user_ev_handler,
|
cdc_acm_user_ev_handler,
|
||||||
CDC_ACM_COMM_INTERFACE,
|
CDC_ACM_COMM_INTERFACE,
|
||||||
@ -73,33 +73,196 @@ APP_USBD_CDC_ACM_GLOBAL_DEF(m_app_cdc_acm,
|
|||||||
CDC_ACM_DATA_EPOUT,
|
CDC_ACM_DATA_EPOUT,
|
||||||
APP_USBD_CDC_COMM_PROTOCOL_AT_V250);
|
APP_USBD_CDC_COMM_PROTOCOL_AT_V250);
|
||||||
|
|
||||||
#define READ_SIZE 1
|
__STATIC_INLINE void reset_buffer_count(uint8_t *buffer_start) {
|
||||||
|
buffer_start[1] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
static char m_rx_buffer[READ_SIZE];
|
__STATIC_INLINE void increment_packet(uint8_t *buffer_start) {
|
||||||
static char m_tx_buffer[NRF_DRV_USBD_EPSIZE];
|
buffer_start[1] += 1;
|
||||||
static bool m_send_flag = 0;
|
}
|
||||||
|
|
||||||
|
void reset_counters(void) {
|
||||||
|
#if ADS1298
|
||||||
|
m_info.usb_buffer_count = ADS1298_PACKET_OFFSET;
|
||||||
|
reset_buffer_count(m_info.usb_buffer);
|
||||||
|
#if ADS1298_STATS
|
||||||
|
m_info.drdy_trigger_count = 0;
|
||||||
|
m_info.seconds_elapsed = 0;
|
||||||
|
m_info.bytes_sent_usb = 0;
|
||||||
|
#endif
|
||||||
|
// #TODO: elapsed time Milliseconds (need last second timer).
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// #DO NOT USE THIS. IT BREAKS THE PACKET FORMAT. USE
|
||||||
|
// void usb_send_message_1byte(uint8_t message) {
|
||||||
|
// usb_send_buffer[0] = message;
|
||||||
|
// ret_code_t ret = app_usbd_cdc_acm_write(&m_app_cdc_acm, usb_send_buffer, 1);
|
||||||
|
// NRF_LOG_INFO("[push]Writing message of length %d, ret: %d", 1, ret);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 1. For mushing together small messages into a 64-byte packet:
|
||||||
|
void usb_send_append_message(uint8_t message_prefix, uint8_t* message, uint8_t message_length) {
|
||||||
|
usb_send_buffer[usb_send_buffer_offset] = message_prefix;
|
||||||
|
usb_send_buffer[usb_send_buffer_offset + 1] = message_length + 2;
|
||||||
|
memcpy(&usb_send_buffer[usb_send_buffer_offset + 2], message, message_length);
|
||||||
|
|
||||||
|
usb_send_buffer_offset += (2 + message_length);
|
||||||
|
|
||||||
|
if (usb_send_buffer_offset >= 64) { // end of buffer
|
||||||
|
NRF_LOG_INFO("[WARNING] exceeded limit of usb_send_buffer\n");
|
||||||
|
usb_send_buffer_offset = 64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 2. For pushing all messages queued with usb_send_append_message
|
||||||
|
void usb_send_push(void) {
|
||||||
|
ret_code_t ret = app_usbd_cdc_acm_write(&m_app_cdc_acm, usb_send_buffer, usb_send_buffer_offset);
|
||||||
|
|
||||||
|
NRF_LOG_INFO("[push]Writing message of length %d, ret: %d", usb_send_buffer_offset, ret);
|
||||||
|
|
||||||
|
usb_send_buffer_offset = 0; // reset.
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. For sending two-byte prefixed messages
|
||||||
|
void usb_send_push_ex(uint8_t message_prefix, uint8_t message_part_two, uint8_t* message, uint8_t message_length) {
|
||||||
|
usb_send_buffer[0] = message_prefix;
|
||||||
|
usb_send_buffer[1] = message_part_two;
|
||||||
|
usb_send_buffer[2] = message_length + 3;
|
||||||
|
memcpy(&usb_send_buffer[3], message, message_length);
|
||||||
|
ret_code_t ret = app_usbd_cdc_acm_write(&m_app_cdc_acm, usb_send_buffer, message_length + 3);
|
||||||
|
|
||||||
|
NRF_LOG_INFO("[push ex]Writing message of length %d, ret: %d", message_length + 3, ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// length is always `READ_SIZE`
|
||||||
|
void write_ic_settings(uint8_t* new_packet) {
|
||||||
|
switch (SECOND_NIBBLE(new_packet[0])) {
|
||||||
|
case TN_IC_ADS1298: {
|
||||||
|
// #TODO: &new_packet[1]
|
||||||
|
#if ADS1298
|
||||||
|
ads1298_stop_rdatac();
|
||||||
|
NRF_LOG_INFO("Writing new ADS1298 registers:");
|
||||||
|
NRF_LOG_HEXDUMP_INFO(&new_packet[1], ADS1298_REGISTER_COUNT);
|
||||||
|
memcpy(m_info.registers, &new_packet[1], ADS1298_REGISTER_COUNT);
|
||||||
|
ads1298_update_registers(&m_info);
|
||||||
|
ads1298_set_data_buffer_length(&m_info);
|
||||||
|
ads1298_start_rdatac();
|
||||||
|
ads1298_standby();
|
||||||
|
// #TODO: readback registers into m_info.registers to confirm correct write.
|
||||||
|
uint8_t message[] = {0x00}; // LENGTH OF ZERO.
|
||||||
|
usb_send_push_ex(CTRL_ACK_WRITE_IC_REGISTER, IC_ID_ADS1298, message, strlen(message));
|
||||||
|
#endif
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void read_ic_settings(uint8_t* new_packet) {
|
||||||
|
switch (SECOND_NIBBLE(new_packet[0])) {
|
||||||
|
case TN_IC_ADS1298: {
|
||||||
|
// #NOTE: you will not be able to read registers while in RDATAC mode.
|
||||||
|
|
||||||
|
// ads1298_check_id(&m_info);
|
||||||
|
ads1298_stop_rdatac();
|
||||||
|
// read all registers into m_info.registers
|
||||||
|
ads1298_readback_registers(&m_info);
|
||||||
|
// Send back over USB.
|
||||||
|
usb_send_push_ex(CTRL_REGISTER_READBACK, IC_ID_ADS1298,
|
||||||
|
m_info.registers, ADS1298_REGISTER_COUNT);
|
||||||
|
ads1298_start_rdatac();
|
||||||
|
ads1298_standby();
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// length is always `READ_SIZE`
|
||||||
|
static void process_new_packet(uint8_t* new_packet) {
|
||||||
|
switch (FIRST_NIBBLE(new_packet[0])) {
|
||||||
|
case CN_WRITE_IC_REGS: {
|
||||||
|
write_ic_settings(new_packet);
|
||||||
|
} break;
|
||||||
|
case CN_READ_IC_REGS: {
|
||||||
|
read_ic_settings(new_packet);
|
||||||
|
} break;
|
||||||
|
case CN_STREAM_CONTROL: {
|
||||||
|
if (SECOND_NIBBLE(new_packet[0]) == TN_STREAM_START) {
|
||||||
|
recording_mode = RECORDING_MODE_ALL;
|
||||||
|
#if ADS1298
|
||||||
|
if (m_info.state == 0) {
|
||||||
|
m_info.state = 1;
|
||||||
|
// ads1298_update_registers(&m_info);
|
||||||
|
// ads1298_set_data_buffer_length(&m_info);
|
||||||
|
ads1298_wakeup();
|
||||||
|
NRF_LOG_INFO("[ADS129x] Active channels: 0x%X", m_info.active_chs);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
reset_counters();
|
||||||
|
}
|
||||||
|
if (SECOND_NIBBLE(new_packet[0]) == TN_STREAM_STOP) {
|
||||||
|
recording_mode = RECORDING_MODE_DISABLED;
|
||||||
|
#if ADS1298
|
||||||
|
if (m_info.state == 1) {
|
||||||
|
m_info.state = 0;
|
||||||
|
ads1298_standby();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if ADS1298_STATS
|
||||||
|
NRF_LOG_INFO("Bytes sent via USB: %lu\n", m_info.bytes_sent_usb);
|
||||||
|
#endif
|
||||||
|
reset_counters();
|
||||||
|
}
|
||||||
|
if (SECOND_NIBBLE(new_packet[0]) == TN_STREAM_REQUEST_PERIPHERAL_INFO) {
|
||||||
|
// #TODO: we should assert that the length of these messages does not exceed the packet
|
||||||
|
// size!
|
||||||
|
usb_send_append_message(CTRL_STAT_INFO_PERIPHERALS, g_Peripheral_Info, strlen(g_Peripheral_Info));
|
||||||
|
usb_send_append_message(CTRL_STAT_INFO_WHO_AM_I, g_WhoAmI, strlen(g_WhoAmI));
|
||||||
|
usb_send_append_message(CTRL_STAT_INFO_HW_FW_VERSION, g_HWFW_Ver, sizeof(g_HWFW_Ver));
|
||||||
|
usb_send_push();
|
||||||
|
}
|
||||||
|
if (SECOND_NIBBLE(new_packet[0]) == TN_STREAM_REQUEST_BATTERY_LEVEL) {
|
||||||
|
// #NOTE: Will not use. This is not a battery-powered device. (I mean, it can
|
||||||
|
// be, but it's also wired, so who cares.)
|
||||||
|
// Just return some constant.
|
||||||
|
}
|
||||||
|
|
||||||
|
} break;
|
||||||
|
case CN_MISC_CONTROLS: {
|
||||||
|
if (SECOND_NIBBLE(new_packet[0]) == TN_MISC_THROUGHPUT_TEST) { // 0xEF
|
||||||
|
NRF_LOG_INFO("Starting throughput test!");
|
||||||
|
run_throughput_test = true;
|
||||||
|
}
|
||||||
|
if (SECOND_NIBBLE(new_packet[0]) == TN_MISC_RTT_REQUEST) {
|
||||||
|
uint8_t message[] = {0x00}; // LENGTH OF ZERO.
|
||||||
|
usb_send_append_message(CTRL_ACK_RTT_REQUEST, message, strlen(message));
|
||||||
|
usb_send_push();
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief User event handler @ref app_usbd_cdc_acm_user_ev_handler_t (headphones)
|
|
||||||
* */
|
|
||||||
static void cdc_acm_user_ev_handler(app_usbd_class_inst_t const * p_inst,
|
static void cdc_acm_user_ev_handler(app_usbd_class_inst_t const * p_inst,
|
||||||
app_usbd_cdc_acm_user_event_t event) {
|
app_usbd_cdc_acm_user_event_t event) {
|
||||||
app_usbd_cdc_acm_t const * p_cdc_acm = app_usbd_cdc_acm_class_get(p_inst);
|
app_usbd_cdc_acm_t const * p_cdc_acm = app_usbd_cdc_acm_class_get(p_inst);
|
||||||
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case APP_USBD_CDC_ACM_USER_EVT_PORT_OPEN: {
|
case APP_USBD_CDC_ACM_USER_EVT_PORT_OPEN: {
|
||||||
|
|
||||||
/*Setup first transfer*/
|
/*Setup first transfer*/
|
||||||
ret_code_t ret = app_usbd_cdc_acm_read(&m_app_cdc_acm,
|
ret_code_t ret = app_usbd_cdc_acm_read(&m_app_cdc_acm, usb_read_buffer, READ_SIZE);
|
||||||
m_rx_buffer,
|
|
||||||
READ_SIZE);
|
|
||||||
UNUSED_VARIABLE(ret);
|
UNUSED_VARIABLE(ret);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case APP_USBD_CDC_ACM_USER_EVT_PORT_CLOSE:
|
case APP_USBD_CDC_ACM_USER_EVT_PORT_CLOSE:
|
||||||
break;
|
break;
|
||||||
case APP_USBD_CDC_ACM_USER_EVT_TX_DONE:
|
case APP_USBD_CDC_ACM_USER_EVT_TX_DONE:
|
||||||
tx_complete = true;
|
|
||||||
if (m_send_logs && full_tx_complete) { reset_log_buffer(); }
|
|
||||||
break;
|
break;
|
||||||
case APP_USBD_CDC_ACM_USER_EVT_RX_DONE: {
|
case APP_USBD_CDC_ACM_USER_EVT_RX_DONE: {
|
||||||
ret_code_t ret;
|
ret_code_t ret;
|
||||||
@ -107,23 +270,13 @@ static void cdc_acm_user_ev_handler(app_usbd_class_inst_t const * p_inst,
|
|||||||
do {
|
do {
|
||||||
/*Get amount of data transfered*/
|
/*Get amount of data transfered*/
|
||||||
size_t size = app_usbd_cdc_acm_rx_size(p_cdc_acm);
|
size_t size = app_usbd_cdc_acm_rx_size(p_cdc_acm);
|
||||||
NRF_LOG_INFO("RX: size: %lu char: %c", size, m_rx_buffer[0]);
|
|
||||||
|
|
||||||
if (m_rx_buffer[0] == 0) {
|
|
||||||
m_send_flag = false;
|
|
||||||
m_send_logs = false;
|
|
||||||
}
|
|
||||||
if (m_rx_buffer[0] == 1) {
|
|
||||||
m_send_flag = true;
|
|
||||||
}
|
|
||||||
if (m_rx_buffer[0] == 2) {
|
|
||||||
m_send_logs = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
NRF_LOG_INFO("RX[size:%lu] [0]: 0x%X", size, usb_read_buffer[0]);
|
||||||
|
|
||||||
|
process_new_packet(usb_read_buffer);
|
||||||
|
|
||||||
/* Fetch data until internal buffer is empty */
|
/* Fetch data until internal buffer is empty */
|
||||||
ret = app_usbd_cdc_acm_read(&m_app_cdc_acm,
|
ret = app_usbd_cdc_acm_read(&m_app_cdc_acm, usb_read_buffer, READ_SIZE);
|
||||||
m_rx_buffer,
|
|
||||||
READ_SIZE);
|
|
||||||
} while (ret == NRF_SUCCESS);
|
} while (ret == NRF_SUCCESS);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -134,6 +287,7 @@ static void cdc_acm_user_ev_handler(app_usbd_class_inst_t const * p_inst,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void usbd_user_ev_handler(app_usbd_event_type_t event) {
|
static void usbd_user_ev_handler(app_usbd_event_type_t event) {
|
||||||
|
// #TODO: should reset_counters() somewhere here?
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case APP_USBD_EVT_DRV_SUSPEND:
|
case APP_USBD_EVT_DRV_SUSPEND:
|
||||||
break;
|
break;
|
||||||
@ -164,43 +318,26 @@ static void usbd_user_ev_handler(app_usbd_event_type_t event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Send_USB_Logs(void) {
|
#if ADS1298
|
||||||
if (!tx_complete) return;
|
|
||||||
// We should check that the USB data is
|
|
||||||
s32 bytes_remaining = m_log_usage - m_log_buffer_send_index;
|
|
||||||
if (bytes_remaining <= 0) {
|
|
||||||
full_tx_complete = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
app_usbd_cdc_acm_write(&m_app_cdc_acm, &log_buffer[m_log_buffer_send_index], 64);
|
|
||||||
m_log_buffer_send_index += 64;
|
|
||||||
tx_complete = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if ADS1298 || ADS1292
|
|
||||||
void drdy_pin_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action) {
|
void drdy_pin_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action) {
|
||||||
UNUSED_PARAMETER(pin);
|
UNUSED_PARAMETER(pin);
|
||||||
UNUSED_PARAMETER(action);
|
UNUSED_PARAMETER(action);
|
||||||
JIIM_LOG("drdy!");
|
drdy_flag = true;
|
||||||
// drdy_flag = true;
|
#if ADS1298_STATS
|
||||||
}
|
m_info.drdy_trigger_count += 1;
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
#if ADS1298
|
void ads1298_interrupt_setup(void) {
|
||||||
ads1298_info_t m_info;
|
|
||||||
|
|
||||||
void ads1298_init_gpio(void) {
|
|
||||||
nrf_gpio_cfg_output(ADS1298_PWDN_PIN);
|
nrf_gpio_cfg_output(ADS1298_PWDN_PIN);
|
||||||
nrf_gpio_pin_clear(ADS1298_PWDN_PIN);
|
nrf_gpio_pin_clear(ADS1298_PWDN_PIN);
|
||||||
nrf_gpio_cfg_input(ADS1298_DRDY_PIN, NRF_GPIO_PIN_PULLUP);
|
nrf_gpio_cfg_input(ADS1298_DRDY_PIN, NRF_GPIO_PIN_PULLUP);
|
||||||
// Initialize GPIOTE:
|
// Initialize GPIOTE:
|
||||||
ret_code_t err_code = NRF_SUCCESS;
|
ret_code_t err_code = NRF_SUCCESS;
|
||||||
if (!nrf_drv_gpiote_is_init())
|
if (!nrf_drv_gpiote_is_init()) {
|
||||||
{
|
|
||||||
err_code = nrf_drv_gpiote_init();
|
err_code = nrf_drv_gpiote_init();
|
||||||
}
|
}
|
||||||
JIIM_LOG("GPIOTE error code: %d", err_code);
|
NRF_LOG_INFO("GPIOTE error code: %d", err_code);
|
||||||
APP_ERROR_CHECK(err_code);
|
APP_ERROR_CHECK(err_code);
|
||||||
nrfx_gpiote_in_config_t in_config = NRFX_GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
|
nrfx_gpiote_in_config_t in_config = NRFX_GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
|
||||||
in_config.is_watcher = true;
|
in_config.is_watcher = true;
|
||||||
@ -212,30 +349,38 @@ void ads1298_init_gpio(void) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if ADS1292
|
#if ADS1298_STATS
|
||||||
ads129x_info_t m_info;
|
APP_TIMER_DEF(ads_timer_id);
|
||||||
|
#define APP_TIMER_INTERVAL APP_TIMER_TICKS(1000) // 1 second.
|
||||||
|
static volatile bool ads_timer_timeout = false;
|
||||||
|
|
||||||
void ads1292_interrupt_setup(void) {
|
static void ads_timer_timeout_handler(void* p_context) {
|
||||||
nrf_gpio_cfg_output(ADS1292_PWDN_PIN);
|
UNUSED_PARAMETER(p_context);
|
||||||
nrf_gpio_pin_clear(ADS1292_PWDN_PIN); // Powers down the ADS1292
|
|
||||||
nrf_gpio_cfg_input(ADS1292_DRDY_PIN, NRF_GPIO_PIN_PULLUP);
|
ads_timer_timeout = true;
|
||||||
// Initialize GPIOTE:
|
m_info.seconds_elapsed += 1;
|
||||||
ret_code_t err_code = NRF_SUCCESS;
|
|
||||||
if (!nrf_drv_gpiote_is_init()) {
|
|
||||||
err_code = nrf_drv_gpiote_init();
|
|
||||||
}
|
|
||||||
NRF_LOG_INFO("GPIOTE error code: %d", err_code);
|
|
||||||
APP_ERROR_CHECK(err_code);
|
|
||||||
nrfx_gpiote_in_config_t in_config = NRFX_GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
|
|
||||||
in_config.is_watcher = true;
|
|
||||||
in_config.pull = NRF_GPIO_PIN_NOPULL;
|
|
||||||
err_code = nrf_drv_gpiote_in_init(ADS1292_DRDY_PIN, &in_config, drdy_pin_handler);
|
|
||||||
APP_ERROR_CHECK(err_code);
|
|
||||||
nrf_drv_gpiote_in_event_enable(ADS1292_DRDY_PIN, true);
|
|
||||||
ads1292_power_down();
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static void application_timers_start(void) {
|
||||||
|
#if ADS1298_STATS
|
||||||
|
ret_code_t err_code;
|
||||||
|
err_code = app_timer_start(ads_timer_id, APP_TIMER_INTERVAL, NULL);
|
||||||
|
APP_ERROR_CHECK(err_code);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static void timers_init(void) {
|
||||||
|
ret_code_t ret = app_timer_init();
|
||||||
|
APP_ERROR_CHECK(ret);
|
||||||
|
|
||||||
|
#if ADS1298_STATS
|
||||||
|
ret_code_t err_code =
|
||||||
|
app_timer_create(&ads_timer_id, APP_TIMER_MODE_REPEATED, ads_timer_timeout_handler);
|
||||||
|
APP_ERROR_CHECK(err_code);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
ret_code_t ret;
|
ret_code_t ret;
|
||||||
static const app_usbd_config_t usbd_config = {
|
static const app_usbd_config_t usbd_config = {
|
||||||
@ -245,6 +390,8 @@ int main(void) {
|
|||||||
ret = NRF_LOG_INIT(NULL);
|
ret = NRF_LOG_INIT(NULL);
|
||||||
APP_ERROR_CHECK(ret);
|
APP_ERROR_CHECK(ret);
|
||||||
|
|
||||||
|
NRF_LOG_DEFAULT_BACKENDS_INIT(); // Init RTT Backend
|
||||||
|
|
||||||
ret = nrf_drv_clock_init();
|
ret = nrf_drv_clock_init();
|
||||||
APP_ERROR_CHECK(ret);
|
APP_ERROR_CHECK(ret);
|
||||||
|
|
||||||
@ -253,14 +400,13 @@ int main(void) {
|
|||||||
// Wait for LFCLK to init.
|
// Wait for LFCLK to init.
|
||||||
while(!nrf_drv_clock_lfclk_is_running()) { }
|
while(!nrf_drv_clock_lfclk_is_running()) { }
|
||||||
|
|
||||||
ret = app_timer_init();
|
timers_init();
|
||||||
APP_ERROR_CHECK(ret);
|
|
||||||
|
|
||||||
app_usbd_serial_num_generate();
|
app_usbd_serial_num_generate();
|
||||||
|
|
||||||
ret = app_usbd_init(&usbd_config);
|
ret = app_usbd_init(&usbd_config);
|
||||||
APP_ERROR_CHECK(ret);
|
APP_ERROR_CHECK(ret);
|
||||||
// NRF_LOG_INFO("USBD CDC ACM example started.");
|
NRF_LOG_INFO("USBD CDC ACM example started.");
|
||||||
|
|
||||||
app_usbd_class_inst_t const * class_cdc_acm = app_usbd_cdc_acm_class_inst_get(&m_app_cdc_acm);
|
app_usbd_class_inst_t const * class_cdc_acm = app_usbd_cdc_acm_class_inst_get(&m_app_cdc_acm);
|
||||||
ret = app_usbd_class_append(class_cdc_acm);
|
ret = app_usbd_class_append(class_cdc_acm);
|
||||||
@ -270,62 +416,67 @@ int main(void) {
|
|||||||
ret = app_usbd_power_events_enable();
|
ret = app_usbd_power_events_enable();
|
||||||
APP_ERROR_CHECK(ret);
|
APP_ERROR_CHECK(ret);
|
||||||
} else {
|
} else {
|
||||||
JIIM_LOG("No USB power detection enabled\r\nStarting USB now");
|
NRF_LOG_INFO("No USB power detection enabled\r\nStarting USB now");
|
||||||
|
|
||||||
app_usbd_enable();
|
app_usbd_enable();
|
||||||
app_usbd_start();
|
app_usbd_start();
|
||||||
}
|
}
|
||||||
// Throughput testing:
|
|
||||||
reset_log_buffer(); // initialize log buffer with `A`
|
|
||||||
JIIM_LOG("Hello world!");
|
|
||||||
JIIM_LOG("HELLO WORLD %d FORMATTED", 2);
|
|
||||||
JIIM_LOG("sizeof(log_buffer): %d", sizeof(log_buffer));
|
|
||||||
|
|
||||||
JIIM_LOG("Hello, I am a string2");
|
// Init peripherals:
|
||||||
|
|
||||||
#if ADS1292
|
|
||||||
ads1292_interrupt_setup();
|
|
||||||
|
|
||||||
ads1292_initialize(&m_info);
|
|
||||||
#endif
|
|
||||||
#if ADS1298
|
#if ADS1298
|
||||||
ads1298_init_gpio();
|
ads1298_interrupt_setup();
|
||||||
|
nrf_delay_ms(200);
|
||||||
memset(&m_info, 0, sizeof(ads1298_info_t));
|
|
||||||
ads1298_initialize(&m_info);
|
ads1298_initialize(&m_info);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if ADS1292 || ADS1298
|
memset(usb_send_buffer, 0x41, NRF_DRV_USBD_EPSIZE); // #TEMP
|
||||||
if (m_info.nChs > 0) {
|
|
||||||
JIIM_LOG("Number of channels available: %d", m_info.nChs);
|
reset_counters();
|
||||||
} else {
|
|
||||||
JIIM_LOG("ERROR: ADS129x NOT DETECTED!");
|
// #TEMP: application_timers_start
|
||||||
}
|
application_timers_start();
|
||||||
#endif
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
while (app_usbd_event_queue_process()) {/* Nothing to do */}
|
while (app_usbd_event_queue_process()) { /* Nothing to do */ }
|
||||||
|
|
||||||
//if (m_send_flag) {
|
if (recording_mode == RECORDING_MODE_ALL) {
|
||||||
// static int frame_counter;
|
// if (recording_mode & drdy_flag) { // may be faster if we're just using ADS1298
|
||||||
|
#if ADS1298
|
||||||
|
if (drdy_flag) {
|
||||||
|
drdy_flag = false;
|
||||||
|
#if ADS1298_STATS
|
||||||
|
|
||||||
|
if (ads_timer_timeout) {
|
||||||
|
ads_timer_timeout = false;
|
||||||
|
//NRF_LOG_INFO("[ADS1298] Collected %lu samples in %d seconds", m_info.drdy_trigger_count, m_info.seconds_elapsed);
|
||||||
|
NRF_LOG_INFO("[ADS1298] DR: %lu", m_info.drdy_trigger_count / m_info.seconds_elapsed);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// size_t size = sprintf(m_tx_buffer, "Hello USB CDC FA demo: %u\r\n", frame_counter);
|
// :ADS1298_FAST_PATH
|
||||||
|
ads1298_get_data_fast(&m_info);
|
||||||
|
|
||||||
|
if (m_info.usb_buffer_count >= m_info.usb_buffer_size_max) {
|
||||||
|
m_info.usb_buffer_count = ADS1298_PACKET_OFFSET;
|
||||||
|
app_usbd_cdc_acm_write(&m_app_cdc_acm, m_info.usb_buffer, m_info.usb_buffer_size_max);
|
||||||
|
#if ADS1298_STATS
|
||||||
|
m_info.bytes_sent_usb += m_info.usb_buffer_size_max;
|
||||||
|
#endif
|
||||||
|
increment_packet(m_info.usb_buffer);
|
||||||
|
|
||||||
|
// NRF_LOG_INFO("Current time tick: %lu", app_timer_cnt_get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// ret = app_usbd_cdc_acm_write(&m_app_cdc_acm, m_tx_buffer, size);
|
if (run_throughput_test) {
|
||||||
// if (ret == NRF_SUCCESS) { ++frame_counter; }
|
app_usbd_cdc_acm_write(&m_app_cdc_acm, usb_send_buffer, NRF_DRV_USBD_EPSIZE);
|
||||||
|
}
|
||||||
// app_usbd_cdc_acm_write(&m_app_cdc_acm, m_tx_buffer, size);
|
|
||||||
// send_usb_log(hello_string);
|
|
||||||
//log_usb("TestTestTest");
|
|
||||||
// count += 1;
|
|
||||||
//}
|
|
||||||
|
|
||||||
if (m_send_logs) { Send_USB_Logs(); }
|
|
||||||
|
|
||||||
UNUSED_RETURN_VALUE(NRF_LOG_PROCESS());
|
UNUSED_RETURN_VALUE(NRF_LOG_PROCESS());
|
||||||
|
|
||||||
/* Sleep CPU only if there was no interrupt since last loop processing */
|
/* Sleep CPU only if there was no interrupt since last loop processing */
|
||||||
__WFE();
|
__WFE();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @} */
|
|
||||||
|
|||||||
92
pca10056/blank/config/custom_board.h
Normal file
92
pca10056/blank/config/custom_board.h
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
#ifndef CUSTOM_BOARD_H
|
||||||
|
#define CUSTOM_BOARD_H
|
||||||
|
|
||||||
|
#define NRF52840_BREAKOUT_BOARD 1
|
||||||
|
#define ADS1298 1
|
||||||
|
#if ADS1298
|
||||||
|
#define ADS1298_STATS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Testing with nRF52840:
|
||||||
|
#if NRF52840_BREAKOUT_BOARD
|
||||||
|
#define ADS1298_DRDY_PIN 11
|
||||||
|
#define ADS1298_MISO_PIN 12
|
||||||
|
#define ADS1298_SCK_PIN 13
|
||||||
|
#define ADS1298_CS_PIN 14
|
||||||
|
// I don't think you're supposed to tie PWDN and reset together on ADS1298
|
||||||
|
#define ADS1298_PWDN_PIN 15
|
||||||
|
#define ADS1298_MOSI_PIN 16
|
||||||
|
|
||||||
|
// This is the packet format for data received over USB:
|
||||||
|
// [CN:4bit][TN:4bit][..data]
|
||||||
|
|
||||||
|
// COMMAND NIBBLES (4-bit definitions), CN_
|
||||||
|
#define CN_WRITE_IC_REGS 0x8 // 0b1000
|
||||||
|
#define CN_READ_IC_REGS 0x4 // 0b0100
|
||||||
|
#define CN_STREAM_CONTROL 0xB // 0b1011
|
||||||
|
#define CN_MISC_CONTROLS 0xE // 0b1110
|
||||||
|
|
||||||
|
// CTRL_REQUEST_BATTERY_LEVEL :: 0xBB
|
||||||
|
|
||||||
|
// TARGET NIBBLES (4-bit definitions)
|
||||||
|
#define TN_IC_ADS1298 0x1
|
||||||
|
#define TN_IC_EXT 0xF // Extended .. check the next byte!
|
||||||
|
|
||||||
|
#define TN_STREAM_START 0x2
|
||||||
|
#define TN_STREAM_STOP 0xF
|
||||||
|
#define TN_STREAM_REQUEST_BATTERY_LEVEL 0xB
|
||||||
|
// #define TN_STREAM_REQUEST_WHO_AM_I 0xD
|
||||||
|
#define TN_STREAM_REQUEST_PERIPHERAL_INFO 0xE
|
||||||
|
|
||||||
|
#define TN_MISC_THROUGHPUT_TEST 0xF
|
||||||
|
#define TN_MISC_RTT_REQUEST 0x1
|
||||||
|
|
||||||
|
// #################### RESPONSE CODES ####################
|
||||||
|
// #NOTE: 0x01 and 0x02 are reserved (for now) for legacy BLE reasons. I should deprecate and
|
||||||
|
// remove these.
|
||||||
|
#define CTRL_STAT_INFO_STRING 0x01
|
||||||
|
#define CTRL_STAT_INFO_BYTES_SENT 0x02
|
||||||
|
#define CTRL_STAT_INFO_PERIPHERALS 0x03
|
||||||
|
#define CTRL_STAT_INFO_HW_FW_VERSION 0x04
|
||||||
|
#define CTRL_STAT_INFO_WHO_AM_I 0x05
|
||||||
|
|
||||||
|
#define CTRL_STAT_INFO_BATTERY_LEVEL 0xB1
|
||||||
|
// For register readback, the following bytes will be ic-dependent, as
|
||||||
|
// some devices have more than 62 bytes worth of registers, so it may
|
||||||
|
// need to be split up into pages.
|
||||||
|
#define CTRL_REGISTER_READBACK 0xC8
|
||||||
|
#define CTRL_ACK_WRITE_IC_REGISTER 0xDF
|
||||||
|
#define CTRL_ACK_RTT_REQUEST 0xE0
|
||||||
|
|
||||||
|
// %%% IC Identifiers %%%
|
||||||
|
// I want a SINGLE table for 8-bit IDs corresponding to different ICs
|
||||||
|
// These values can be anything from 0x06:0xFF, as long as it doesn't conflict
|
||||||
|
// with any of the reserved ones from above.
|
||||||
|
|
||||||
|
// If the USB packet leads [0] with the ID, then it is a data packet
|
||||||
|
// in the typical format of [ID, Serial, ...data] where the length of data
|
||||||
|
// should be statically determined in advance.
|
||||||
|
// For example, with the ADS1298, the packet length is determined based on the
|
||||||
|
// channel count (see: ads1298_set_data_buffer_length for more details)
|
||||||
|
// #define RESPONSE_PACKET 0x00
|
||||||
|
#define IC_ID_ADS1298 0xD1
|
||||||
|
#define IC_ID_EXT 0xFF // see next byte for extended table
|
||||||
|
|
||||||
|
#define FIRST_NIBBLE(x) ((x >> 4) & 0xF)
|
||||||
|
#define SECOND_NIBBLE(x) (x & 0xF)
|
||||||
|
// For IC controls, we want to prefix the registers with the length: [u16???]
|
||||||
|
// Data format will be different for each device.
|
||||||
|
// For ADS1298
|
||||||
|
// [CN:4bit][TN:4bit][length:u8][..data]
|
||||||
|
// For other device with 1 register read/write at a time
|
||||||
|
// [CN:4bit][TN:4bit][RegisterID][RegisterData] for single register
|
||||||
|
|
||||||
|
#endif // NRF52840_BREAKOUT_BOARD
|
||||||
|
|
||||||
|
enum recording_mode_t {
|
||||||
|
RECORDING_MODE_DISABLED = 0,
|
||||||
|
RECORDING_MODE_ALL = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // CUSTOM_BOARD_H
|
||||||
@ -1628,7 +1628,7 @@
|
|||||||
// <i> This may limit throughput if a lot of binary data is sent, but in terminal mode operation it makes sure that the data is always displayed right after it is sent.
|
// <i> This may limit throughput if a lot of binary data is sent, but in terminal mode operation it makes sure that the data is always displayed right after it is sent.
|
||||||
|
|
||||||
#ifndef APP_USBD_CDC_ACM_ZLP_ON_EPSIZE_WRITE
|
#ifndef APP_USBD_CDC_ACM_ZLP_ON_EPSIZE_WRITE
|
||||||
#define APP_USBD_CDC_ACM_ZLP_ON_EPSIZE_WRITE 1
|
#define APP_USBD_CDC_ACM_ZLP_ON_EPSIZE_WRITE 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// </h>
|
// </h>
|
||||||
@ -1771,7 +1771,7 @@
|
|||||||
// <e> NRF_LOG_BACKEND_RTT_ENABLED - nrf_log_backend_rtt - Log RTT backend
|
// <e> NRF_LOG_BACKEND_RTT_ENABLED - nrf_log_backend_rtt - Log RTT backend
|
||||||
//==========================================================
|
//==========================================================
|
||||||
#ifndef NRF_LOG_BACKEND_RTT_ENABLED
|
#ifndef NRF_LOG_BACKEND_RTT_ENABLED
|
||||||
#define NRF_LOG_BACKEND_RTT_ENABLED 0
|
#define NRF_LOG_BACKEND_RTT_ENABLED 1
|
||||||
#endif
|
#endif
|
||||||
// <o> NRF_LOG_BACKEND_RTT_TEMP_BUFFER_SIZE - Size of buffer for partially processed strings.
|
// <o> NRF_LOG_BACKEND_RTT_TEMP_BUFFER_SIZE - Size of buffer for partially processed strings.
|
||||||
// <i> Size of the buffer is a trade-off between RAM usage and processing.
|
// <i> Size of the buffer is a trade-off between RAM usage and processing.
|
||||||
@ -1780,7 +1780,7 @@
|
|||||||
// <i> longer one will be fragmented.
|
// <i> longer one will be fragmented.
|
||||||
|
|
||||||
#ifndef NRF_LOG_BACKEND_RTT_TEMP_BUFFER_SIZE
|
#ifndef NRF_LOG_BACKEND_RTT_TEMP_BUFFER_SIZE
|
||||||
#define NRF_LOG_BACKEND_RTT_TEMP_BUFFER_SIZE 64
|
#define NRF_LOG_BACKEND_RTT_TEMP_BUFFER_SIZE 256
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// <o> NRF_LOG_BACKEND_RTT_TX_RETRY_DELAY_MS - Period before retrying writing to RTT
|
// <o> NRF_LOG_BACKEND_RTT_TX_RETRY_DELAY_MS - Period before retrying writing to RTT
|
||||||
@ -3092,7 +3092,7 @@
|
|||||||
|
|
||||||
|
|
||||||
#ifndef SPI0_USE_EASY_DMA
|
#ifndef SPI0_USE_EASY_DMA
|
||||||
#define SPI0_USE_EASY_DMA 1
|
#define SPI0_USE_EASY_DMA 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// </e>
|
// </e>
|
||||||
@ -3114,7 +3114,7 @@
|
|||||||
// <e> SPI2_ENABLED - Enable SPI2 instance
|
// <e> SPI2_ENABLED - Enable SPI2 instance
|
||||||
//==========================================================
|
//==========================================================
|
||||||
#ifndef SPI2_ENABLED
|
#ifndef SPI2_ENABLED
|
||||||
#define SPI2_ENABLED 1
|
#define SPI2_ENABLED 0
|
||||||
#endif
|
#endif
|
||||||
// <q> SPI2_USE_EASY_DMA - Use EasyDMA
|
// <q> SPI2_USE_EASY_DMA - Use EasyDMA
|
||||||
|
|
||||||
|
|||||||
@ -44,9 +44,9 @@
|
|||||||
<folder Name="Application">
|
<folder Name="Application">
|
||||||
<file file_name="../../../ads1298.c" />
|
<file file_name="../../../ads1298.c" />
|
||||||
<file file_name="../../../ads1298.h" />
|
<file file_name="../../../ads1298.h" />
|
||||||
|
<file file_name="../config/custom_board.h" />
|
||||||
<file file_name="../../../main.c" />
|
<file file_name="../../../main.c" />
|
||||||
<file file_name="../config/sdk_config.h" />
|
<file file_name="../config/sdk_config.h" />
|
||||||
<file file_name="../../../usb_logging.c" />
|
|
||||||
</folder>
|
</folder>
|
||||||
<folder Name="Board Definition">
|
<folder Name="Board Definition">
|
||||||
<file file_name="../../../../../../components/boards/boards.c" />
|
<file file_name="../../../../../../components/boards/boards.c" />
|
||||||
|
|||||||
24
pca10056/blank/ses/build_and_run.bat
Normal file
24
pca10056/blank/ses/build_and_run.bat
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
@echo off
|
||||||
|
echo building project with emBuild:
|
||||||
|
emBuild -time -rebuild -nostderr -config "Release" -solution "Multimodal_CV_USB_ADS1298R_pca10056" Multimodal_CV_USB_ADS1298R_pca10056.emProject
|
||||||
|
IF ERRORLEVEL 1 (
|
||||||
|
echo Building solution failed.
|
||||||
|
echo.
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
echo build success.
|
||||||
|
|
||||||
|
nrfjprog --program Output\Release\Exe\Multimodal_CV_USB_ADS1298R_pca10056.hex --chiperase --verify --reset
|
||||||
|
IF ERRORLEVEL 1 (
|
||||||
|
echo Flashing target device failed.
|
||||||
|
echo.
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
echo flash success.
|
||||||
|
|
||||||
|
rem "C:\Program Files\SEGGER\JLink_V810f\JLinkRTTViewer.exe" --autoconnect -d "NRF52840_XXAA"
|
||||||
|
rem IF ERRORLEVEL 1 (
|
||||||
|
rem echo J-Link RTT Viewer failed to launch.
|
||||||
|
rem echo.
|
||||||
|
rem exit /b 1
|
||||||
|
rem )
|
||||||
24
pca10056/blank/ses/debug_build_and_run.bat
Normal file
24
pca10056/blank/ses/debug_build_and_run.bat
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
@echo off
|
||||||
|
echo building project with emBuild:
|
||||||
|
emBuild -time -rebuild -nostderr -config "Debug" -solution "Multimodal_CV_USB_ADS1298R_pca10056" Multimodal_CV_USB_ADS1298R_pca10056.emProject
|
||||||
|
IF ERRORLEVEL 1 (
|
||||||
|
echo Building solution failed.
|
||||||
|
echo.
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
echo build success.
|
||||||
|
|
||||||
|
nrfjprog --program Output\Debug\Exe\Multimodal_CV_USB_ADS1298R_pca10056.hex --chiperase --verify --reset
|
||||||
|
IF ERRORLEVEL 1 (
|
||||||
|
echo Flashing target device failed.
|
||||||
|
echo.
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
echo flash success.
|
||||||
|
|
||||||
|
"C:\Program Files\SEGGER\JLink_V810f\JLinkRTTViewer.exe" --autoconnect -d "NRF52840_XXAA"
|
||||||
|
IF ERRORLEVEL 1 (
|
||||||
|
echo J-Link RTT Viewer failed to launch.
|
||||||
|
echo.
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
@ -1,29 +0,0 @@
|
|||||||
#include "base/usb_logging.h"
|
|
||||||
|
|
||||||
void reset_log_buffer(void) {
|
|
||||||
// If debug, then memset buffer
|
|
||||||
memset(log_buffer, 0, sizeof(log_buffer));
|
|
||||||
m_log_usage = 0;
|
|
||||||
m_log_buffer_send_index = 0;
|
|
||||||
tx_complete = true;
|
|
||||||
full_tx_complete = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_to_log_buffer(String str) {
|
|
||||||
// Bounds check the buffer:
|
|
||||||
if (m_log_usage + str.count >= sizeof(log_buffer)) {
|
|
||||||
// Failure!.. How to handle? Turn on LEDs?
|
|
||||||
return; // Do not add to buffer
|
|
||||||
}
|
|
||||||
memcpy(&log_buffer[m_log_usage], str.data, str.count);
|
|
||||||
m_log_usage += str.count;
|
|
||||||
}
|
|
||||||
|
|
||||||
// #TODO: Inline
|
|
||||||
void add_format_string_to_log_buffer(void) {
|
|
||||||
// Replace null terminator with \n where required.
|
|
||||||
sprint_buffer[sprint_length] = '\n';
|
|
||||||
String s = {sprint_length+1, sprint_buffer};
|
|
||||||
add_to_log_buffer(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user