/***************************************************************************//** @addtogroup user_process @{ @file l502_user_process.c Файл содержит простейший вариант реализации пользовательских функций, в котором потоки данных передаются без изменения и никакие пользовательские команды не обрабатываются. Пользователь может изменить этот файл и добавить здесь свою обработку. *******************************************************************************/ #include "l502_stream.h" #include "l502_hdma.h" #include "l502_sport_tx.h" #include "l502_cmd.h" #include #include "l502_defs.h" // import defines of constants #include "l502_params.h" #include "l502_stream.h" #include "l502_user_process.h" uint32_t streams_cnt[4] = {0,}; #define LFSM_val_ON 0b01100111 #define LFSM_val_OFF 0b01101000 #define LFSM_val_X 0b01101001 //#define TX_BUFF_SIZE 1024*1024 //#define TX_BUFF_SIZE 1000 #define LFSM_BUFF_SIZE 1000 #define LFSM_DATALEN 1024 #define dbg_sport_rx_copy_size 10 //#define LFSM_DATALEN 1024 volatile uint32_t dbg_receive_value = 1234321; //#pragma section("sdram_noinit", NO_INIT) //#include "l502_sdram_noinit.h" //__attribute__((section(".sdram_noinit"), far)) //static volatile uint32_t dbg_sport_rx_copy[TX_BUFF_SIZE]; //uint32_t dbg_sport_rx_copy[TX_BUFF_SIZE];// = {0,}; uint32_t dbg_sport_rx_copy[dbg_sport_rx_copy_size];// = {0,}; //* //#include "l502_sdram_noinit.h" //static volatile uint32_t LFSM_data[LFSM_DATALEN] = {0,}; //#include "l502_sdram_noinit.h" //static volatile uint32_t TX_buff[TX_BUFF_SIZE]; //static volatile uint32_t TX_buff[TX_BUFF_SIZE] __attribute__((section(".sdram_noinit"))); //static volatile uint32_t TX_buff[10000] __attribute__((section(".sdram_noinit"))); //static volatile uint32_t TX_buff[10000]; //static volatile uint32_t // = {0,}; uint32_t streams_succes_flag = 0; /* struct dataprocess_typedef { uint8_t config = 0; uint32_t datapoints_max_N = 0; uint32_t datapoint_curr_I = 0; uint32_t data_raw; }; */ extern volatile uint32_t TX_buff[]; //size: TX_BUFF_SIZE extern volatile uint32_t TX_buff_shadow[]; //size: TX_BUFF_SIZE //extern volatile uint32_t RAW_data_buff[]; //size: RAW_DATA_BUFF_SIZE extern volatile uint32_t AVG_buff[]; //size: AVG_BUFF_SIZE //extern volatile uint32_t AVG_buff_B[]; //size: AVG_BUFF_SIZE extern volatile uint64_t FFT_buff[]; //size: FFT_BUFF_SIZE extern volatile int64_t twiddle_re[]; //size: TWIDDLE_L extern volatile int64_t twiddle_im[]; //size: TWIDDLE_L volatile uint32_t TX_marker[10] = {0xFE00000F,0xFE0000F0, 0xFE000F00, 0xFE00F000, 0xFE0F0000,0xFEF00000, 0xFE0F0000,0xFE00F000, 0xFE000F00, 0xFE0000F0}; volatile struct dataprocessor_dypedef{ uint8_t mode; uint8_t mode_next; uint8_t cycle_state; uint8_t LFSM_state; // 0 -- waiting for it... ; 1 -- receiving LFSM data uint16_t average_N_max; uint16_t average_N; uint32_t TX_buff_I; uint8_t TX_buff_state; //0 --blocked, 1 -- filling, 2 -- ready to send uint32_t AVG_buff_I; uint32_t AVG_buff_I_last; uint8_t AVG_state; uint8_t AVG_buff_state; uint8_t AVG_buff_active; uint32_t FFT_buff_I; uint8_t FFT_buff_state; uint32_t digital_word_prev; uint32_t digital_word_curr; uint8_t DIN2_SYN_value_curr; uint8_t DIN2_SYN_value_prev; }Proc_state; volatile uint32_t TX_buff_I = 0; volatile uint32_t TX_buff_I_shadow = 123321; volatile uint32_t data_I = 0; volatile uint32_t send_size = 0; volatile uint8_t TX_buff_state = TX_DONE; //FILLING, TODO_TX, TRANSMITTING, TX_DONE, BUFF_READY, volatile uint32_t tx_val = 0; uint32_t some_i = 0; volatile uint32_t size_processed = 0; //int f_sport_test(void); void l502_stream_init(void); //struct LFSM_typedef{ // uint8_t //}; uint8_t LFSM_started = 0; uint8_t DY_SYN_2_value = 0; uint8_t DY_SYN_2_value_prev = 0; uint32_t dataprocessor(uint32_t* AVG_buff, uint32_t AVG_buff_I_max, uint64_t* FFT_buff, uint8_t mode){ uint32_t FFT_buff_I = 0; if (mode == TRANSPARENT){ uint32_t i = 0; while ((FFT_buff_I < FFT_BUFF_SIZE) && (i < AVG_BUFF_SIZE)){ FFT_buff[FFT_buff_I++] = AVG_buff[i++]; } }else if (mode == FFT){ //uint32_t i = 0; //void FFT_fp(int64_t* inp, uint32_t inp_L, int64_t* buf) for (uint32_t i = 0; i < FFT_INP_DATA_L; ++i){ AVG_buff[i] = FP_acc; } FFT_fp(AVG_buff, FFT_INP_DATA_L, FFT_buff); }else if (mode == FLUSH_TWIDDLES){ uint32_t i = 0; FFT_buff_I = 0; while ((FFT_buff_I < FFT_BUFF_SIZE) && (i < TWIDDLE_L)){ FFT_buff[FFT_buff_I++] = twiddle_re[i++]; } } return FFT_buff_I; } /***************************************************************************//** @brief Обработка принятого массива данных АЦП/DIN. Функция вызывается каждый раз, когда обнаружены новые данные от АЦП/цифровых входов, пришедшие по SPORT0. Функция должна обработать данные и вернуть количество обработанных данных, однако эти данные все еще считаются использованными (не могут быть переписаны новыми пришедшими данными) до тех пор пока не будет вызвана функция stream_in_buf_free()). Если функция вернет значение меньше чем size, то функция будут вызванна при следующем проходе еще раз с указателем на необработанные данные. В текущей реализации просто запускается передача данных по HDMA в ПК @param[in] data Указатель на массив с принятыми данными @param[in] size Количество принятых данных в 32-битных словах @return Функция возвращает количество обработанных данных (от 0 до size). На эти данные не будет вызываться повторно usr_in_proc_data(), но они считаются еще используемыми *******************************************************************************/ uint32_t usr_in_proc_data(uint32_t* data, uint32_t size) { /* если есть свободные дескрипторы на передачу по HDMA - ставим блок на передачу. Иначе возвращаем 0, чтобы на обработку этих данных функцию вызвали бы позже */ ++streams_cnt[0]; //* size_processed = 0; //clear TX_buff if (TX_buff_state == TX_DONE){ for (int i = 0; i < TX_BUFF_SIZE; ++i){ //TX_buff[i] = 0x00000123; TX_buff[i] = tx_val++ | 0xEE000000; //TX_buff[i] = size; } TX_buff_state = FILLING; } if (Proc_state.mode == TRANSPARENT){ TX_buff_state = TX_BUFF_OFF; if (hdma_send_req_rdy()){ hdma_send_req_start(data, size, 0); return size; }else{ return 0; } } //simple transparent mode if (Proc_state.mode == SEMITRANSPARENT){ //++Proc_state.average_N; if (TX_buff_state == FILLING){ data_I = 0; TX_buff_I = 0; while((data_I + 1 < size )&& (TX_buff_I + 1 < TX_BUFF_SIZE)){ //; TX_buff[TX_buff_I] = data[data_I]; TX_buff_I++; data_I++; } size_processed = data_I; TX_buff_state = TODO_TX; //tx_val = 0; } //TX_buff_state = DUMMY; } //TX_buff_I = TX_BUFF_SIZE; if (Proc_state.mode == WORK){ } if (Proc_state.mode == AVG){ while(++data_I < size ){ uint32_t word = data[data_I]; uint32_t val = word & 0x00FFFFFF; uint8_t header = (uint8_t)(word >> 24); if (header == 0x00){ //digital_channel. switches LFSM state machine DY_SYN_2_value_prev = DY_SYN_2_value; if (word & (0b1 << 17)){ DY_SYN_2_value = 1; }else{ DY_SYN_2_value = 0; } if ((DY_SYN_2_value == 1)&& (DY_SYN_2_value_prev == 0)){ //new cycle started //data[data_I] = 0xB00000000; Proc_state.AVG_buff_I = 0; Proc_state.LFSM_state = CYCLE_STARTED; if (Proc_state.average_N >= Proc_state.average_N_max){ //whole average ended //Proc_state.average_N = 0; //if (1){ Proc_state.AVG_state = FULLY_COMPLETED; for(uint32_t i = 0; i < FFT_BUFF_SIZE; i++){ FFT_buff[i] = 0; } uint32_t FFT_res_size = dataprocessor(AVG_buff, Proc_state.AVG_buff_I_last, FFT_buff, FFT); // uint32_t FFT_res_size = dataprocessor(AVG_buff, Proc_state.AVG_buff_I_last, FFT_buff, FLUSH_TWIDDLES); // uint32_t FFT_res_size = dataprocessor(AVG_buff, Proc_state.AVG_buff_I_last, FFT_buff, TRANSPARENT); //averaging completed => copy average results to TX_buff and start avg again TX_buff_I = 0; // for (uint32_t i = 0; ((TX_buff_I < TX_BUFF_SIZE) && (i < AVG_BUFF_SIZE)); i++){ uint32_t i = 0; // while ((TX_buff_I < TX_BUFF_SIZE) && (i < AVG_BUFF_SIZE)){ // TX_buff[TX_buff_I++] = AVG_buff[i++]; while ((TX_buff_I < TX_BUFF_SIZE) && (i < FFT_BUFF_SIZE)){ TX_buff[TX_buff_I++] = 0xFF000000 | ((uint32_t)FFT_buff[i++]); //TX_buff[TX_buff_I++] = 0xB0000000 + Proc_state.average_N; //TX_buff[TX_buff_I++] = 0xC1000000; } //clear AVG_buff: for (uint32_t i = 0; i < AVG_BUFF_SIZE; i++ ){ AVG_buff[i] = 0xE0000000; } TX_buff_state = TODO_TX; // for (uint32_t i = 0; i < TX_BUFF_SIZE; ++i){ // TX_buff_shadow[i] = TX_buff[i]; // } // hdma_send_req_start(TX_buff_shadow, TX_buff_I, 0); // hdma_send_req_start(TX_marker, 10, 0); // hdma_send_req_start(TX_buff, TX_BUFF_SIZE, 0); //TX_buff_state = TRANSMITTING; // TX_buff_state = TX_DONE; Proc_state.average_N = 0; //hdma_send_req_start(TX_buff, TX_buff_I, 0); //return data_I; }else{ // Proc_state.AVG_state = STEP_RUNNING; Proc_state.average_N ++; //Proc_state.AVG_buff_I = 0; } } }else if(header == 0xD0){ //first phy channel if (Proc_state.AVG_state == STEP_RUNNING){ // if (1){ // if (Proc_state.LFSM_state == CYCLE_STARTED){ if (1){ // AVG_buff[Proc_state.AVG_buff_I++] = 0xC0000000 | (0x00FFFFFF & (AVG_buff[Proc_state.AVG_buff_I] + val)); AVG_buff[Proc_state.AVG_buff_I] = 0xC0000000 | ((val/Proc_state.average_N_max + AVG_buff[Proc_state.AVG_buff_I]) & 0xFFFFFF); // AVG_buff[Proc_state.AVG_buff_I] = 0xC0000000 | some_i++; // AVG_buff[Proc_state.AVG_buff_I] = 0xC0000000 | val; //AVG_buff[Proc_state.AVG_buff_I++] = 0xC0000000; Proc_state.AVG_buff_I_last = Proc_state.AVG_buff_I; Proc_state.AVG_buff_I++; if (Proc_state.AVG_buff_I >= AVG_BUFF_SIZE){ Proc_state.AVG_state = STEP_COMPLETED; } } } }else{ //AVG_buff[Proc_state.AVG_buff_I] = 0xC1000000; Proc_state.AVG_buff_I_last = Proc_state.AVG_buff_I; Proc_state.AVG_buff_I++; if (Proc_state.AVG_buff_I >= AVG_BUFF_SIZE){ Proc_state.AVG_state = STEP_COMPLETED; } } } } //*/ if (hdma_send_req_rdy()) { //if (1){ if (TX_buff_state == TX_BUFF_OFF){ hdma_send_req_start(data, size, 0); return size; }else{ if (TX_buff_state == TODO_TX){ for (uint32_t i = 0; i < TX_BUFF_SIZE; ++i){ TX_buff_shadow[i] = TX_buff[i]; } hdma_send_req_start(TX_buff_shadow, TX_buff_I, 0); hdma_send_req_start(TX_marker, 10, 0); //hdma_send_req_start(TX_buff, TX_BUFF_SIZE, 0); //TX_buff_state = TRANSMITTING; TX_buff_state = TX_DONE; //}else{ // hdma_send_req_start(data, size, 0); } //hdma_send_req_start(data, size, 0); //streams_cnt[0] = hdma_send_req_start(LFSM_data, LFSM_DATALEN, 0); return size_processed; //return data_I; //number of really processed words } } //return data_I; //number of really processed words return 0; } /***************************************************************************//** @brief Обработка принятого массива с данными ЦАП/DOUT Функция вызывается каждый раз, когда обнаружены новые данные, принятые от ПК по HDMA. Функция должна обработать данные и вернуть количество обработанных данных, однако эти данные все еще считаются использованными (не могут быть переписаны новыми пришедшими данными) до тех пор пока не будет вызвана функция stream_out_buf_free()). Если функция вернет значение меньше чем size, то функция будут вызвана после еще раз с указателем на необработанные данные. В текущей реализации просто запускается передача данных по SPORT для вывода на ЦАП/цифровые выходы. @param[in] data Указатель на массив с принятыми данными @param[in] size Количество принятых данных в 32-битных словах @return Функция возвращает количество обработанных данных (от 0 до size). На эти данные не будет вызываться повторно usr_out_proc_data(), но они считаются еще используемыми ******************************************************************************/ uint32_t usr_out_proc_data(uint32_t* data, uint32_t size) { /* если есть свободные дескрипторы на передачу по HDMA - ставим блок на передачу. Иначе возвращаем 0, чтобы на обработку этих данных функцию вызвали бы позже */ ++streams_cnt[2]; if (sport_tx_req_rdy()) { /* за один раз можем передать в SPORT не более SPORT_TX_REQ_SIZE_MAX слов */ if (size > SPORT_TX_REQ_SIZE_MAX) size = SPORT_TX_REQ_SIZE_MAX; sport_tx_start_req(data, size); return size; } return 0; } /****************************************************************************//** @brief Обработка завершения передачи по HostDMA Функция вызывается из обработчика прерывания, когда завершилась передача блока данных по HDMA в ПК, поставленного до этого на передачу с помощью hdma_send_req_start(). @param[in] addr Адрес слова, сразу за последним переданным словом @param[in] size Размер переданных данных в 32-битных словах ****************************************************************************/ void hdma_send_done(uint32_t* addr, uint32_t size) { TX_buff_state = TX_DONE; ++streams_cnt[3]; stream_in_buf_free(size); } /***************************************************************************//** @brief Обработка завершения передачи по SPORT Функция вызывается из обработчика прерывания при завершении передачи блока данных по SPORT'у на цифровые выходы/ЦАП, поставленного до этого на передачу с помощью sport_tx_start_req(). @param[in] addr Адрес слова, сразу за последним переданным словом @param[in] size Размер переданных данных в 32-битных словах */ void sport_tx_done(uint32_t* addr, uint32_t size) { ++streams_cnt[1]; stream_out_buf_free(size); } /****************************************************************************//** @brief Обработка пользовательских команд. Функция вызывается при приеме команды от ПК с кодом большим или равным #L502_BF_CMD_CODE_USER. По завершению обработки необходимо обязательно вызвать l502_cmd_done(), указав код завершения команды и при необходимости передать данные с результатом @param[in] cmd Структура с описанием принятой команды ******************************************************************************/ void usr_cmd_process(t_l502_bf_cmd *cmd) { switch (cmd->code){ /* Command template case 0x800?:{ l502_cmd_done(rcv_code, rcv_data, rcv_data_length); //void l502_cmd_done (int32_t result, uint32_t *data, uint32_t size) break; } */ // typedef struct { // uint16_t code; /**< Код команды из #t_l502_bf_cmd_code */ // uint16_t status; /**< Статус выполнения - в обработчике не изменяется */ // uint32_t param; /**< Параметр команды */ // int32_t result; /**< Код результата выполнения команды */ // uint32_t data_size; /**< Количество данных, переданных с командой или возвращенных с ответом в 32-битных словах */ // uint32_t data[L502_BF_CMD_DATA_SIZE_MAX]; /**< Данные, передаваемые с командой и/или в качестве результата */ // } t_l502_bf_cmd; //* case 0x8001:{ //L502_BF_USR_CMD_CODE_ECHO l502_cmd_done(cmd-> param, NULL, 0); break; } case 0x8002:{ //L502_BF_USR_CMD_CODE_DATA_ECHO uint32_t rcv_data[cmd->data_size]; for (uint32_t I = 0; I < (cmd -> data_size); ++I){ //rcv_data[I] = (cmd-> data)[I]; rcv_data[I] = I; } l502_cmd_done(cmd-> param, rcv_data, cmd->data_size); break; } case 0x8003:{ //configure ADC uint32_t err_codes[15] = {0,}; err_codes[0] = params_set_lch_cnt(1); //err_codes[1] = params_set_lch(1, 1, L502_LCH_MODE_COMM, L502_ADC_RANGE_5, 1, 0);// chan index 1, chan 1, mode L502_LCH_MODE_COMM (=1),range L502_ADC_RANGE_5 (=1), avg, flags err_codes[1] = params_set_lch(0, 0, L502_LCH_MODE_COMM, L502_ADC_RANGE_5, 1, 0);// chan index 1, chan 1, mode L502_LCH_MODE_COMM (=1),range L502_ADC_RANGE_5 (=1), avg, flags err_codes[2] = params_set_adc_freq_div(1); err_codes[3] = params_set_ref_freq(2000000); err_codes[4] = params_set_adc_interframe_delay(0); err_codes[5] = params_set_sync_mode (L502_SYNC_INTERNAL); err_codes[6] = params_set_sync_start_mode (L502_SYNC_INTERNAL); //err_codes[7] = params_set_din_freq_div; //err_codes[8] = params_set_dac_freq_div; err_codes[9] = sport_in_set_step_size(1024); //L502_BF_PARAM_ADC_COEF //err_codes[10] = fpga_reg_write(f_regaddr_k[range], ??); //err_codes[11] = fpga_reg_write(f_regaddr_offs[range], ??); err_codes[12] = configure(); uint32_t err_codes_sum = 0; for (int i = 0; i < 15; ++i){ err_codes_sum += err_codes[i]; } l502_cmd_done(err_codes_sum, err_codes, 15); break; } case 0x8004:{ //start streams uint32_t err_codes[5] = {0,}; l502_stream_init(); err_codes[0] = stream_enable(L502_STREAM_ADC | L502_STREAM_DIN); //bitmask err_codes[1] = stream_out_preload(); err_codes[2] = streams_start(); //hdma_send_start(); l502_cmd_done(0, err_codes, 5); break; } case 0x8005:{ //get some data from adc buff. Or simple flag, raised inside usr_in_proc_data() uint32_t streams_sum = streams_cnt[0] + streams_cnt[1] + streams_cnt[2] + streams_cnt[3]; l502_cmd_done(streams_sum, streams_cnt, 4); break; } case 0x8006:{ //get data from SPORT_RX copied arr uint32_t sport_rx_nonzero_sum = 0; for (int i = 0; i < dbg_sport_rx_copy_size; ++i){ if (dbg_sport_rx_copy[i]){ ++sport_rx_nonzero_sum; } } l502_cmd_done(sport_rx_nonzero_sum, dbg_sport_rx_copy, 1024); break; } case 0x8007:{ //start data processing: No dataprocessing. Just copy data to output buffer TX_buff_I = 0; TX_buff_state = TX_BUFF_OFF; Proc_state.mode = TRANSPARENT; Proc_state.mode_next = TRANSPARENT; Proc_state.LFSM_state = CYCLE_UNKNOWN; Proc_state.average_N_max = 10; // Proc_state.average_N_max = cmd->param; Proc_state.average_N = 1; Proc_state.TX_buff_I = 0; Proc_state.TX_buff_state = 0; Proc_state.AVG_state = 0; Proc_state.AVG_buff_I = 0; Proc_state.AVG_buff_state = 0; Proc_state.FFT_buff_I = 0; Proc_state.FFT_buff_state = 0; Proc_state.digital_word_prev = 0; Proc_state.digital_word_curr = 0; Proc_state.AVG_buff_active = A; TX_buff_state = TX_DONE; for (uint32_t i = 0; i < TX_BUFF_SIZE; ++i){ TX_buff[i] = 0; } // l502_cmd_done(cmd-> param, NULL, 0); l502_cmd_done(TX_buff_I, NULL, 0); break; } case 0x8008:{ //AVG mode TX_buff_I = 0; Proc_state.mode = AVG; Proc_state.mode_next = AVG; Proc_state.LFSM_state = CYCLE_UNKNOWN; Proc_state.average_N_max = 1; // Proc_state.average_N_max = cmd->param; Proc_state.average_N = 0; Proc_state.TX_buff_I = 0; Proc_state.TX_buff_state = 0; Proc_state.AVG_state = 0; Proc_state.AVG_buff_I = 0; Proc_state.AVG_buff_state = 0; Proc_state.FFT_buff_I = 0; Proc_state.FFT_buff_state = 0; Proc_state.digital_word_prev = 0; Proc_state.digital_word_curr = 0; Proc_state.AVG_buff_active = A; TX_buff_state = TX_DONE; for (uint32_t i = 0; i < TX_BUFF_SIZE; ++i){ TX_buff[i] = 0; } // l502_cmd_done(cmd-> param, NULL, 0); l502_cmd_done(TX_buff_I, NULL, 0); break; } case 0x8009:{ //request TX_buff_I_shadow value (last size of TX_buff transferred to pc ) l502_cmd_done(TX_buff_I_shadow, NULL, 0); break; } case 0x800A:{ //return uint32_t value stored in dbg_receive_value l502_cmd_done(dbg_receive_value, NULL, 0); break; } case 0x800B:{// SEMITRANSPARENT mode TX_buff_I = 0; Proc_state.mode = SEMITRANSPARENT; Proc_state.mode_next = SEMITRANSPARENT; Proc_state.LFSM_state = CYCLE_UNKNOWN; Proc_state.average_N_max = 10; // Proc_state.average_N_max = cmd->param; Proc_state.average_N = 1; Proc_state.TX_buff_I = 0; Proc_state.TX_buff_state = 0; Proc_state.AVG_state = 0; Proc_state.AVG_buff_I = 0; Proc_state.AVG_buff_state = 0; Proc_state.FFT_buff_I = 0; Proc_state.FFT_buff_state = 0; Proc_state.digital_word_prev = 0; Proc_state.digital_word_curr = 0; Proc_state.AVG_buff_active = A; TX_buff_state = TX_DONE; for (uint32_t i = 0; i < TX_BUFF_SIZE; ++i){ TX_buff[i] = 0; } // l502_cmd_done(cmd-> param, NULL, 0); l502_cmd_done(TX_buff_I, NULL, 0); break; } case 0x8010:{ //flush HDMA TX buffer int number_of_free_tx_descriptors = hdma_send_req_rdy(); hdma_send_stop(); hdma_send_start(); l502_cmd_done(number_of_free_tx_descriptors, NULL, 0); break; } case 0x8011:{ //L502_BF_USR_CMD_CODE_ECHO l502_cmd_done(cmd-> param, NULL, 0); break; } case 0x8012:{ //L502_BF_USR_CMD_CODE_ECHO l502_cmd_done(cmd-> param, NULL, 0); break; } case 0x8013:{ //L502_BF_USR_CMD_CODE_ECHO l502_cmd_done(cmd-> param, NULL, 0); break; } case 0x8014:{ //L502_BF_USR_CMD_CODE_ECHO l502_cmd_done(cmd-> param, NULL, 0); break; } case 0x8015:{ //L502_BF_USR_CMD_CODE_ECHO l502_cmd_done(cmd-> param, NULL, 0); break; } /* case 0x8002:{ cmd-> result = cmd->param * 2; uint32_t data[] = {0,1,2,3,4,5,6,5,4,3,2,1,0}; l502_cmd_done(cmd-> param*3, data, 13); break;} case 0x8001:{ //cmd-> result = cmd->param * 2; uint32_t data[] = {6,5,4,3,2,1,0,1,2,3,4,5,6}; l502_cmd_done(cmd-> param*75, data, 13); break;} */ default: { l502_cmd_done(1, NULL, 0); } } //l502_cmd_done(1, 1, 1); //l502_cmd_done(L502_BF_ERR_UNSUP_CMD, NULL, 0); } /** @} */