From 919523a46517d2bef0d30ff1c4857a6594a87c2e Mon Sep 17 00:00:00 2001 From: Musa Mahmood Date: Thu, 17 Apr 2025 07:14:22 -0400 Subject: [PATCH] Allow buffering of small messages (e.g. peripheral info, who-am-i, and hw/fw version) over USB --- ads1298.c | 14 ++- ads1298.h | 5 +- main.c | 136 +++++++++++++++++++-------- pca10056/blank/config/custom_board.h | 9 +- pca10056/blank/config/sdk_config.h | 2 +- 5 files changed, 119 insertions(+), 47 deletions(-) diff --git a/ads1298.c b/ads1298.c index 5ff373f..1fce6e0 100644 --- a/ads1298.c +++ b/ads1298.c @@ -76,10 +76,11 @@ void ads1298_initialize(ads1298_info_t *p_info) { spi_config.miso_pin = ADS1298_MISO_PIN; spi_config.mosi_pin = ADS1298_MOSI_PIN; spi_config.sck_pin = ADS1298_SCK_PIN; - //spi_config.frequency = NRF_DRV_SPI_FREQ_125K; + spi_config.frequency = NRF_DRV_SPI_FREQ_250K; //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.irq_priority = APP_IRQ_PRIORITY_HIGH; //spi_config.irq_priority = APP_IRQ_PRIORITY_HIGHEST; spi_config.mode = NRF_DRV_SPI_MODE_1; spi_config.orc = 0x55; @@ -487,6 +488,15 @@ __STATIC_INLINE void copy_relevant_data(ads1298_info_t* p_info) { } } +void ads1298_get_data_fast(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(); } + memcpy(&p_info->usb_buffer[p_info->usb_buffer_count], &rx_buffer[3], 8 * 3); + p_info->usb_buffer_count += 3 * 8; +} // 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) { diff --git a/ads1298.h b/ads1298.h index 78e5312..d06932d 100644 --- a/ads1298.h +++ b/ads1298.h @@ -65,7 +65,8 @@ #define ADS129x_8CH_BITMASK 0x02 // 0x[...]10[010] /** DEFAULT REGISTER VALUES **/ -#define ADS1298_REGDEFAULT_CONFIG1 0xC6 // High-res 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_CONFIG3 0xCE // #define ADS1298_REGDEFAULT_LOFF 0x00 @@ -112,6 +113,7 @@ typedef struct { #if ADS1298_STATS uint32_t drdy_trigger_count; uint32_t seconds_elapsed; + uint32_t bytes_sent_usb; #endif } ads1298_info_t; @@ -147,6 +149,7 @@ void ads1294_get_data(ads1298_info_t* p_info); void ads1296_get_data(ads1298_info_t* p_info); 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); diff --git a/main.c b/main.c index d3e8710..1c2c4f1 100644 --- a/main.c +++ b/main.c @@ -42,7 +42,8 @@ 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_tx_buffer[NRF_DRV_USBD_EPSIZE]; +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]; @@ -84,30 +85,55 @@ void reset_counters(void) { #if ADS1298 m_info.usb_buffer_count = ADS1298_PACKET_OFFSET; reset_buffer_count(m_info.usb_buffer); - // #TODO: Clear stats: +#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 } -void usb_transmit_message_ex(uint8_t message_prefix, uint8_t message_part_two, uint8_t* message, uint8_t message_length) { - usb_tx_buffer[0] = message_prefix; - usb_tx_buffer[1] = message_part_two; - usb_tx_buffer[2] = message_length + 3; - memcpy(&usb_tx_buffer[3], message, message_length); - ret_code_t ret = app_usbd_cdc_acm_write(&m_app_cdc_acm, usb_tx_buffer, message_length + 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("[ex]Writing message of length %d, ret: %d", message_length + 3, ret); } -void usb_transmit_message(uint8_t message_prefix, uint8_t* message, uint8_t message_length) { - usb_tx_buffer[0] = message_prefix; - usb_tx_buffer[1] = message_length + 2; - memcpy(&usb_tx_buffer[2], message, message_length); - ret_code_t ret = app_usbd_cdc_acm_write(&m_app_cdc_acm, usb_tx_buffer, message_length + 2); +// void usb_transmit_message(uint8_t message_prefix, uint8_t* message, uint8_t message_length) { +// usb_send_buffer[0] = message_prefix; +// usb_send_buffer[1] = message_length + 2; +// memcpy(&usb_send_buffer[2], message, message_length); +// ret_code_t ret = app_usbd_cdc_acm_write(&m_app_cdc_acm, usb_send_buffer, message_length + 2); - NRF_LOG_INFO("[0]Writing message of length %d, ret: %d", message_length + 2, ret); +// NRF_LOG_INFO("[0]Writing message of length %d, ret: %d", message_length + 2, ret); +// } + +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("[1]Writing message of length %d, ret: %d", usb_send_buffer_offset, ret); + + usb_send_buffer_offset = 0; // reset. +} + +// 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; + } } // length is always `READ_SIZE` @@ -142,11 +168,8 @@ void read_ic_settings(uint8_t* new_packet) { // read all registers into m_info.registers ads1298_readback_registers(&m_info); // Send back over USB. - usb_transmit_message_ex(REGISTER_READBACK_PREFIX, REGISTER_READBACK_ADS1298, + usb_send_push_ex(REGISTER_READBACK_PREFIX, REGISTER_READBACK_ADS1298, m_info.registers, ADS1298_REGISTER_COUNT); - // This is totally unnecessary: - // usb_transmit_message_ex(ID_REGISTER_READBACK_PREFIX, REGISTER_READBACK_ADS1298, - // m_info.id_buffer, sizeof(m_info.id_buffer)); ads1298_start_rdatac(); ads1298_standby(); } break; @@ -180,18 +203,24 @@ static void process_new_packet(uint8_t* new_packet) { } if (SECOND_NIBBLE(new_packet[0]) == TN_STREAM_STOP) { recording_mode = RECORDING_MODE_DISABLED; - #if ADS1298 + #if ADS1298 if (m_info.state == 1) { m_info.state = 0; ads1298_standby(); } - #endif + #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_INFO) { - usb_transmit_message(CTRL_STAT_INFO_PERIPHERALS, g_Peripheral_Info, strlen(g_Peripheral_Info)); - usb_transmit_message(CTRL_STAT_INFO_WHO_AM_I, g_WhoAmI, strlen(g_WhoAmI)); - usb_transmit_message(CTRL_STAT_INFO_HW_FW_VERSION, g_HWFW_Ver, sizeof(g_HWFW_Ver)); + 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 @@ -289,7 +318,9 @@ void drdy_pin_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action) { UNUSED_PARAMETER(pin); UNUSED_PARAMETER(action); drdy_flag = true; +#if ADS1298_STATS m_info.drdy_trigger_count += 1; +#endif } void ads1298_interrupt_setup(void) { @@ -393,15 +424,29 @@ int main(void) { ads1298_initialize(&m_info); #endif - memset(usb_tx_buffer, 0x41, NRF_DRV_USBD_EPSIZE); // #TEMP + memset(usb_send_buffer, 0x41, NRF_DRV_USBD_EPSIZE); // #TEMP reset_counters(); // #TEMP: application_timers_start application_timers_start(); +#if AUTO_START_ADS1298 // for debugging + ads1298_stop_rdatac(); + NRF_LOG_INFO("Writing new ADS1298 registers:"); + uint8_t new_registers[] = {0xC5,0x00,0xCE,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x00,0x00}; + memcpy(m_info.registers, new_registers, ADS1298_REGISTER_COUNT); + ads1298_update_registers(&m_info); + ads1298_set_data_buffer_length(&m_info); + ads1298_start_rdatac(); + ads1298_wakeup(); + recording_mode = RECORDING_MODE_ALL; +#endif + while (true) { + #if !DISABLE_USBD_IN_MAIN_LOOP while (app_usbd_event_queue_process()) { /* Nothing to do */ } + #endif if (recording_mode == RECORDING_MODE_ALL) { // if (recording_mode & drdy_flag) { // may be faster if we're just using ADS1298 @@ -417,23 +462,30 @@ int main(void) { } #endif - switch (m_info.nChs) { - case 4: - ads1294_get_data(&m_info); - break; - case 6: - ads1296_get_data(&m_info); - break; - case 8: - ads1298_get_data(&m_info); - break; - default: - break; - } + // :ADS1298_FAST_PATH + ads1298_get_data_fast(&m_info); + // switch (m_info.nChs) { + // case 4: + // ads1294_get_data(&m_info); + // break; + // case 6: + // ads1296_get_data(&m_info); + // break; + // case 8: + // ads1298_get_data(&m_info); + // break; + // default: + // break; + // } if (m_info.usb_buffer_count >= m_info.usb_buffer_size_max) { m_info.usb_buffer_count = ADS1298_PACKET_OFFSET; + #if !DISABLE_USBD_IN_MAIN_LOOP 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 + #endif increment_packet(m_info.usb_buffer); // NRF_LOG_INFO("Current time tick: %lu", app_timer_cnt_get()); @@ -441,14 +493,16 @@ int main(void) { } #endif } - + #if !DISABLE_USBD_IN_MAIN_LOOP if (run_throughput_test) { - /*ret = */app_usbd_cdc_acm_write(&m_app_cdc_acm, usb_tx_buffer, NRF_DRV_USBD_EPSIZE); + /*ret = */app_usbd_cdc_acm_write(&m_app_cdc_acm, usb_send_buffer, NRF_DRV_USBD_EPSIZE); } - + #endif UNUSED_RETURN_VALUE(NRF_LOG_PROCESS()); /* Sleep CPU only if there was no interrupt since last loop processing */ + #if !DISABLE_USBD_IN_MAIN_LOOP __WFE(); + #endif } } diff --git a/pca10056/blank/config/custom_board.h b/pca10056/blank/config/custom_board.h index 77b70d3..bef4717 100644 --- a/pca10056/blank/config/custom_board.h +++ b/pca10056/blank/config/custom_board.h @@ -4,9 +4,13 @@ #define NRF52840_BREAKOUT_BOARD 1 #define ADS1298 1 #if ADS1298 - #define ADS1298_STATS 1 + #define AUTO_START_ADS1298 0 + #define ADS1298_STATS 0 #endif +// Debugging flags +#define DISABLE_USBD_IN_MAIN_LOOP 0 + // Testing with nRF52840: #if NRF52840_BREAKOUT_BOARD #define ADS1298_DRDY_PIN 11 @@ -35,7 +39,8 @@ #define TN_STREAM_START 0x2 #define TN_STREAM_STOP 0xF #define TN_STREAM_REQUEST_BATTERY_LEVEL 0xB - #define TN_STREAM_REQUEST_INFO 0xE + // #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 diff --git a/pca10056/blank/config/sdk_config.h b/pca10056/blank/config/sdk_config.h index c8a4d1b..a1eeff4 100644 --- a/pca10056/blank/config/sdk_config.h +++ b/pca10056/blank/config/sdk_config.h @@ -1628,7 +1628,7 @@ // 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 -#define APP_USBD_CDC_ACM_ZLP_ON_EPSIZE_WRITE 1 +#define APP_USBD_CDC_ACM_ZLP_ON_EPSIZE_WRITE 0 #endif //