#include #include #include #include #include "nrf.h" #include "nrf_drv_usbd.h" #include "nrf_drv_clock.h" #include "nrf_gpio.h" #include "nrf_delay.h" #include "nrf_drv_power.h" #include "app_error.h" #include "app_util.h" #include "app_usbd_core.h" #include "app_usbd.h" #include "app_usbd_string_desc.h" #include "app_usbd_cdc_acm.h" #include "app_usbd_serial_num.h" #include "app_timer.h" #include "nrf_log.h" #include "nrf_log_ctrl.h" #include "nrf_log_default_backends.h" #include "custom_board.h" static enum recording_mode_t recording_mode = RECORDING_MODE_DISABLED; #if ADS1298 #include "ads1298.h" #include "nrf_drv_gpiote.h" static bool drdy_flag = false; ads1298_info_t m_info; #endif // #TEMP! char g_Peripheral_Info[] = "ADS1298|"; 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]; #ifndef USBD_POWER_DETECTION #define USBD_POWER_DETECTION true #endif static void cdc_acm_user_ev_handler(app_usbd_class_inst_t const * p_inst, app_usbd_cdc_acm_user_event_t event); #define CDC_ACM_COMM_INTERFACE 0 #define CDC_ACM_COMM_EPIN NRF_DRV_USBD_EPIN2 #define CDC_ACM_DATA_INTERFACE 1 #define CDC_ACM_DATA_EPIN NRF_DRV_USBD_EPIN1 #define CDC_ACM_DATA_EPOUT NRF_DRV_USBD_EPOUT1 APP_USBD_CDC_ACM_GLOBAL_DEF(m_app_cdc_acm, cdc_acm_user_ev_handler, CDC_ACM_COMM_INTERFACE, CDC_ACM_DATA_INTERFACE, CDC_ACM_COMM_EPIN, CDC_ACM_DATA_EPIN, CDC_ACM_DATA_EPOUT, APP_USBD_CDC_COMM_PROTOCOL_AT_V250); __STATIC_INLINE void reset_buffer_count(uint8_t *buffer_start) { buffer_start[1] = 0; } __STATIC_INLINE void increment_packet(uint8_t *buffer_start) { buffer_start[1] += 1; } 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; } } 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_t const * p_cdc_acm = app_usbd_cdc_acm_class_get(p_inst); switch (event) { case APP_USBD_CDC_ACM_USER_EVT_PORT_OPEN: { /*Setup first transfer*/ ret_code_t ret = app_usbd_cdc_acm_read(&m_app_cdc_acm, usb_read_buffer, READ_SIZE); UNUSED_VARIABLE(ret); break; } case APP_USBD_CDC_ACM_USER_EVT_PORT_CLOSE: break; case APP_USBD_CDC_ACM_USER_EVT_TX_DONE: break; case APP_USBD_CDC_ACM_USER_EVT_RX_DONE: { ret_code_t ret; NRF_LOG_INFO("Bytes waiting: %d", app_usbd_cdc_acm_bytes_stored(p_cdc_acm)); do { /*Get amount of data transfered*/ size_t size = app_usbd_cdc_acm_rx_size(p_cdc_acm); 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 */ ret = app_usbd_cdc_acm_read(&m_app_cdc_acm, usb_read_buffer, READ_SIZE); } while (ret == NRF_SUCCESS); break; } default: break; } } static void usbd_user_ev_handler(app_usbd_event_type_t event) { // #TODO: should reset_counters() somewhere here? switch (event) { case APP_USBD_EVT_DRV_SUSPEND: break; case APP_USBD_EVT_DRV_RESUME: break; case APP_USBD_EVT_STARTED: break; case APP_USBD_EVT_STOPPED: app_usbd_disable(); break; case APP_USBD_EVT_POWER_DETECTED: NRF_LOG_INFO("USB power detected"); if (!nrf_drv_usbd_is_enabled()) { app_usbd_enable(); } break; case APP_USBD_EVT_POWER_REMOVED: NRF_LOG_INFO("USB power removed"); app_usbd_stop(); break; case APP_USBD_EVT_POWER_READY: NRF_LOG_INFO("USB ready"); app_usbd_start(); break; default: break; } } #if ADS1298 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) { nrf_gpio_cfg_output(ADS1298_PWDN_PIN); nrf_gpio_pin_clear(ADS1298_PWDN_PIN); nrf_gpio_cfg_input(ADS1298_DRDY_PIN, NRF_GPIO_PIN_PULLUP); // Initialize GPIOTE: 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(ADS1298_DRDY_PIN, &in_config, drdy_pin_handler); APP_ERROR_CHECK(err_code); nrf_drv_gpiote_in_event_enable(ADS1298_DRDY_PIN, true); ads1298_power_down(); } #endif #if ADS1298_STATS APP_TIMER_DEF(ads_timer_id); #define APP_TIMER_INTERVAL APP_TIMER_TICKS(1000) // 1 second. static volatile bool ads_timer_timeout = false; static void ads_timer_timeout_handler(void* p_context) { UNUSED_PARAMETER(p_context); ads_timer_timeout = true; m_info.seconds_elapsed += 1; } #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) { ret_code_t ret; static const app_usbd_config_t usbd_config = { .ev_state_proc = usbd_user_ev_handler }; ret = NRF_LOG_INIT(NULL); APP_ERROR_CHECK(ret); NRF_LOG_DEFAULT_BACKENDS_INIT(); // Init RTT Backend ret = nrf_drv_clock_init(); APP_ERROR_CHECK(ret); nrf_drv_clock_lfclk_request(NULL); // Wait for LFCLK to init. while(!nrf_drv_clock_lfclk_is_running()) { } timers_init(); app_usbd_serial_num_generate(); ret = app_usbd_init(&usbd_config); APP_ERROR_CHECK(ret); 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); ret = app_usbd_class_append(class_cdc_acm); APP_ERROR_CHECK(ret); if (USBD_POWER_DETECTION) { ret = app_usbd_power_events_enable(); APP_ERROR_CHECK(ret); } else { NRF_LOG_INFO("No USB power detection enabled\r\nStarting USB now"); app_usbd_enable(); app_usbd_start(); } // Init peripherals: #if ADS1298 ads1298_interrupt_setup(); nrf_delay_ms(200); ads1298_initialize(&m_info); #endif 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 #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 // :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()); } } #endif } #if !DISABLE_USBD_IN_MAIN_LOOP if (run_throughput_test) { /*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 } } /** @} */ // size_t size = sprintf(m_tx_buffer, "Hello USB CDC FA demo: %u\r\n", frame_counter);