[SPIM+TIMER+PPI] Issue regarding the handlers for SPIM and TIMER.

Hello,

I am programming BLE with the nRF52840-DK. [Toolchain Manager: v1.3.0, IDE: Visual Studio Code (VSCode), SDK: ncs v2.6.0, window11 pro]

< Simple Code Explanation >

1. The ADC (SLAVE) and nRF52840 DK (MASTER) communicate via SPIM.

2. A command is sent to the ADC at regular intervals defined by 'TIME_TO_WAIT_US'.

  • The TIMER is used to measure the intervals and PPI is used to start SPIM.
  • The command buffer includes tx_buf_array and tx_buf_array_2. (nRF52840 DK -> ADC)
  • tx_buf_array is a command for ADC initialization.
  • tx_buf_array_2 is a command to retrieve ADC data and is intended to be sent repeatedly.
  • The SPIM handler exists to modify Tx_PTR and to signal the end of SPIM.
  • The TIMER handler exists to signal the end of the TIMER.

< my issue>

If TIME_TO_WAIT_US is less than 10, SPIM communication does not work properly. (ADC spec: Maximum SCLK frequency is 25 MHz)

I think a lot of time is being consumed in the handler. I'm not sure exactly what the problem is.

I want SPIM to work properly when 'TIME_TO_WAIT_US' is 5~6.

Could you give me some advice?

- Below is my code for the handler part:

void spim1_handler(nrfx_spim_evt_t const * p_event, void * p_context) {   
	//LOG_INF("spim1_handler[%d] - TXD.PTR: 0x%08X", spi_counter, spim1_inst.p_reg->TXD.PTR);
	if(spim1_inst.p_reg->TXD.PTR == (uint32_t)&tx_buf_array_2[17] + 2) {
        spim1_inst.p_reg->TXD.PTR = (uint32_t)tx_buf_array_2[0];
    } else if(spim1_inst.p_reg->TXD.PTR == (uint32_t)&tx_buf_array[30] ) {
        spim1_inst.p_reg->TXD.PTR = (uint32_t)tx_buf_array_2[0];
    }
	if(spi_counter == MAX_SPI_COUNT){
		spi_disable(spim1_inst.p_reg);
        k_sem_give(&sem_condition1);
    }
    spi_counter++;
}

void timer0_handler(nrf_timer_event_t event_type, void * p_context){   
	if(spi_counter == MAX_SPI_COUNT){
		timer_disable(&timer0_inst);
        k_sem_give(&sem_condition2); 
    }
} 

- Below is my code for the ppi part:

    /*   PPI  Setting  */
    uint32_t gpiote_task_addr = nrfx_gpiote_out_task_address_get(&gpiote_inst ,SS_PIN_MASTER);    
    uint32_t timer_start_compare_event_addr = nrfx_timer_compare_event_address_get(&timer0_inst, NRF_TIMER_CC_CHANNEL0);
    uint32_t spi_start_task_addr   = nrfx_spim_start_task_address_get(&spim1_inst);
    uint32_t spi_end_evt_addr = nrfx_spim_end_event_address_get(&spim1_inst);

    // Timer reaches the desired tick -> GPIOTE toggle(off), SPI start
    error = nrfx_gppi_channel_alloc(&ppi_channel_spi_start);
    nrfx_gppi_channel_endpoints_setup(ppi_channel_spi_start, timer_start_compare_event_addr, gpiote_task_addr);
    nrfx_gppi_fork_endpoint_setup(ppi_channel_spi_start, spi_start_task_addr);

    // SPI tx-rx transmission ends -> GPIOTE toggle(on)
    error = nrfx_gppi_channel_alloc(&ppi_channel_spi_end);
    nrfx_gppi_channel_endpoints_setup(ppi_channel_spi_end, spi_end_evt_addr, gpiote_task_addr);

- below is my 'main.c' code:

#include <nrfx_example.h>
#include <nrfx_spim.h>
#include <nrfx_timer.h>
#include <helpers/nrfx_gppi.h>
#include <nrfx_gpiote.h>
#include <cho_intan_RHD2132.h>
#include <zephyr/logging/log.h>
#include <stdlib.h> 
#define LOG_MODULE_NAME cho
LOG_MODULE_REGISTER(LOG_MODULE_NAME, LOG_LEVEL_DBG);

// Pin definitions
#define MISO_PIN_MASTER 29
#define MOSI_PIN_MASTER 28
#define SCK_PIN_MASTER 4
#define SS_PIN_MASTER 3

// Peripheral instance indexes
#define SPIM_INST_IDX 1
#define TIMER_INST_IDX 0
#define GPIOTE_INST_IDX 0

// Peripheral instances
nrfx_spim_t spim1_inst = NRFX_SPIM_INSTANCE(SPIM_INST_IDX);
nrfx_timer_t timer0_inst = NRFX_TIMER_INSTANCE(TIMER_INST_IDX);
nrfx_gpiote_t gpiote_inst = NRFX_GPIOTE_INSTANCE(GPIOTE_INST_IDX);

// PPI channels
nrf_ppi_channel_t ppi_channel_spi_start;
nrf_ppi_channel_t ppi_channel_spi_end;
nrf_ppi_channel_t ppi_channel_spi_count;

// Counters and flags
volatile int timer_counter = 0;
volatile int spi_counter = 0;
volatile bool stop = false;
volatile bool sample_on = false;

// Semaphores
static K_SEM_DEFINE(sem_condition1, 0, 1);
static K_SEM_DEFINE(sem_condition2, 0, 1);

// INTAN RHD2132 related settings
#define MAX_SPI_COUNT 138
#define TIME_TO_WAIT_US 12
#define ROUND(x) ((x) >= 0 ? (long)((x) + 0.5) : (long)((x) - 0.5))
#define total_sampling_rate ROUND(1000000.0 / TIME_TO_WAIT_US)
#define N_Channel 16
#define N_Aux_command 2
#define per_ch_SAMPLE_RATE  (total_sampling_rate /(N_Channel + N_Aux_command))

double lower_bandwidth = 1;
int upper_bandwidth = 300; 
uint16_t registers[18];

// RHD configuration structure
RHDConfigParameters RHD_config;
RHDConfigParameters* RHD_p = &RHD_config;

// Buffer arrays
uint8_t tx_buf_array[30][2];
uint8_t tx_buf_array_2[18][2];
uint8_t rx_buf_array[2048][2];

// GPIOTE related
uint8_t out_channel;

void spi_disable(NRF_SPIM_Type *p_spim_instance) {

    nrfy_spim_disable(p_spim_instance);
    nrf_spim_int_disable(p_spim_instance, 0xFFFFFFFF);
}

void timer_disable(nrfx_timer_t *p_timer_instance) {

    nrfx_timer_disable(p_timer_instance);
    nrfx_timer_uninit(p_timer_instance);
}

void spim1_handler(nrfx_spim_evt_t const * p_event, void * p_context) {   
	//LOG_INF("spim1_handler[%d] - TXD.PTR: 0x%08X", spi_counter, spim1_inst.p_reg->TXD.PTR);
	if(spim1_inst.p_reg->TXD.PTR == (uint32_t)&tx_buf_array_2[17] + 2) {
        spim1_inst.p_reg->TXD.PTR = (uint32_t)tx_buf_array_2[0];
    } else if(spim1_inst.p_reg->TXD.PTR == (uint32_t)&tx_buf_array[30] ) {
        spim1_inst.p_reg->TXD.PTR = (uint32_t)tx_buf_array_2[0];
    }
	if(spi_counter == MAX_SPI_COUNT){
		spi_disable(spim1_inst.p_reg);
        k_sem_give(&sem_condition1);
    }
    spi_counter++;
}

void timer0_handler(nrf_timer_event_t event_type, void * p_context){   
	if(spi_counter == MAX_SPI_COUNT){
		timer_disable(&timer0_inst);
        k_sem_give(&sem_condition2); 
    }
} 

// GPIOTE, SPIM, TIMER, PPI etc..
void peripheral_setup(void){
    //LOG_INF("main - peripheral_setup");
    nrfx_err_t error;
    (void)error;
    
    /*  GPIOTE Setting */
    static const nrfx_gpiote_output_config_t output_config = {
        .drive = NRF_GPIO_PIN_S0S1,
        .input_connect = NRF_GPIO_PIN_INPUT_DISCONNECT,
        .pull = NRF_GPIO_PIN_NOPULL,
    };
    const nrfx_gpiote_task_config_t task_config = {
        .task_ch = out_channel,
        .polarity = NRF_GPIOTE_POLARITY_TOGGLE,
        .init_val = NRF_GPIOTE_INITIAL_VALUE_HIGH,
    };
    error = nrfx_gpiote_output_configure(&gpiote_inst, SS_PIN_MASTER, &output_config, &task_config);
    nrfx_gpiote_out_task_enable(&gpiote_inst, SS_PIN_MASTER);

    /*  SPIM 1 Setting */
    nrfx_spim_config_t spim1_config = NRFX_SPIM_DEFAULT_CONFIG(SCK_PIN_MASTER,
                                                            MOSI_PIN_MASTER,
                                                            MISO_PIN_MASTER,
                                                            NRF_SPIM_PIN_NOT_CONNECTED);
    spim1_config.frequency      = NRFX_MHZ_TO_HZ(8);
    error = nrfx_spim_init(&spim1_inst, &spim1_config, spim1_handler,0);
    nrfx_spim_xfer_desc_t spim1_xfer_desc = NRFX_SPIM_XFER_TRX((uint8_t*)tx_buf_array, 2, (uint8_t*)rx_buf_array, 2);
    
    uint32_t spim1_flags = NRFX_SPIM_FLAG_HOLD_XFER | NRFX_SPIM_FLAG_REPEATED_XFER; 
    error = nrfx_spim_xfer(&spim1_inst, &spim1_xfer_desc, spim1_flags);
    spim1_inst.p_reg->TXD.PTR = (uint32_t)tx_buf_array[0];
    spim1_inst.p_reg->TXD.MAXCNT = 2;
    spim1_inst.p_reg->TXD.LIST =SPIM_TXD_LIST_LIST_ArrayList << SPIM_TXD_LIST_LIST_Pos;
    spim1_inst.p_reg->RXD.PTR = (uint32_t)rx_buf_array[0];
    spim1_inst.p_reg->RXD.MAXCNT = 2;
    spim1_inst.p_reg->RXD.LIST = SPIM_RXD_LIST_LIST_ArrayList << SPIM_RXD_LIST_LIST_Pos;
   
    /*  TIMER 0 Setting */
    nrfx_timer_config_t timer0_config = NRFX_TIMER_DEFAULT_CONFIG(16000000);
    timer0_config.bit_width          = NRF_TIMER_BIT_WIDTH_32,
    error = nrfx_timer_init(&timer0_inst, &timer0_config, timer0_handler);
    nrfx_timer_clear(&timer0_inst);
    uint32_t desired_ticks = nrfx_timer_us_to_ticks(&timer0_inst, TIME_TO_WAIT_US);
    nrfx_timer_extended_compare(&timer0_inst, NRF_TIMER_CC_CHANNEL0, desired_ticks, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK, true);

    /*   PPI  Setting  */
    uint32_t gpiote_task_addr = nrfx_gpiote_out_task_address_get(&gpiote_inst ,SS_PIN_MASTER);    
    uint32_t timer_start_compare_event_addr = nrfx_timer_compare_event_address_get(&timer0_inst, NRF_TIMER_CC_CHANNEL0);
    uint32_t spi_start_task_addr   = nrfx_spim_start_task_address_get(&spim1_inst);
    uint32_t spi_end_evt_addr = nrfx_spim_end_event_address_get(&spim1_inst);

    // Timer reaches the desired tick -> GPIOTE toggle(off), SPI start
    error = nrfx_gppi_channel_alloc(&ppi_channel_spi_start);
    nrfx_gppi_channel_endpoints_setup(ppi_channel_spi_start, timer_start_compare_event_addr, gpiote_task_addr);
    nrfx_gppi_fork_endpoint_setup(ppi_channel_spi_start, spi_start_task_addr);

    // SPI tx-rx transmission ends -> GPIOTE toggle(on)
    error = nrfx_gppi_channel_alloc(&ppi_channel_spi_end);
    nrfx_gppi_channel_endpoints_setup(ppi_channel_spi_end, spi_end_evt_addr, gpiote_task_addr);
}

void set_default_rhd_settings(RHDConfigParameters* p){
	// Reg 0
	p->adc_reference_bw = 3; p->amp_fast_settle = 0; p->amp_vref_enable = 1; p->adc_comparator_bias = 3; p->adc_comparator_select = 2;
	// Reg 1~2 
	p->vdd_sense_enable = 0; set_ADCbufferbias_MUXbias(p, total_sampling_rate);
	// Reg 3 
	p->mux_load = 0; p->temp_S1 = 0; p->temp_S2 = 0; p->temp_en = 0; p->digOut = 0; p->digOut_hiZ = 1;
	// Reg 4 DSP 수정
	p->weak_miso = 1; p->twos_comp = 0; p->abs_mode = 0; p->DSP_en = 0; set_DSP_cutoff_freq(p, 5.0); // p->DSP_cutoff_freq = DSP cutoff freq 인덱스 저장
	// Reg 5
	p->zcheck_DAC_power = 0; p->zcheck_load = 0; p->zcheck_scale = 0x00; p->zcheck_conn_all = 0; p->zcheck_sel_pol = 0 ; p->zcheck_en = 0;
	// Reg 6~7   Zcheck DAC는 get_register_value 함수에 존재
	p->zcheck_select = 0;
	// Reg 8~13  upper: 300Hz  Lower:1Hz
	p->off_chip_RH1 = 0; p->off_chip_RH2 = 0; p->off_chip_RL = 0;
	p->adc_Aux1_en = 0; p->adc_Aux2_en = 0; p->adc_Aux3_en = 0;
	set_bandwidthREG(p, upper_bandwidth, lower_bandwidth);

	// Reg 14~17
	for (int channel = 0; channel < N_Channel; ++channel) {
		p->amp_pwr[channel] = 1;
	}
    for (int channel = N_Channel; channel < 32; ++channel) {
		p->amp_pwr[channel] = 0;
	}
}

void initialization_command(){
    RHD_p->sample_rate = per_ch_SAMPLE_RATE; 
	set_default_rhd_settings(RHD_p); 
    for (int i = 0; i < 18; i++) {
	registers[i] = get_register_value(RHD_p, i);
    }

    tx_buf_array[0][0] = upper_8bit(read_command(63)); // Read(63) = 0xFF00
    tx_buf_array[0][1] = lower_8bit(read_command(63));  
    tx_buf_array[1][0] = upper_8bit(read_command(63)); // Read(63) = 0xFF00
    tx_buf_array[1][1] = lower_8bit(read_command(63));  

    for (int i = 2; i < 20; i++) { // Write()
	    tx_buf_array[i][0] = upper_8bit(write_command(i-2, registers[i-2])); 
        tx_buf_array[i][1] = lower_8bit(write_command(i-2, registers[i-2]));
    }
    tx_buf_array[20][0] = upper_8bit(calibrate_command()); //Calibrate() = 0x5500
    tx_buf_array[20][1] = lower_8bit(calibrate_command());

    for (int i = 21; i < 30; i++) { // Read(63) = 0xFF00
	    tx_buf_array[i][0] = upper_8bit(read_command(63));
        tx_buf_array[i][1] = lower_8bit(read_command(63));
    }

	for (int i = 0; i < 16 ; i++){
		tx_buf_array_2[i][0] = upper_8bit(convert_command(i, 0));
		tx_buf_array_2[i][1] = lower_8bit(convert_command(i, 0));
	}
	tx_buf_array_2[16][0] = upper_8bit(read_command(63));
	tx_buf_array_2[16][1] = lower_8bit(read_command(63));
	tx_buf_array_2[17][0] = upper_8bit(read_command(63));
	tx_buf_array_2[17][1] = lower_8bit(read_command(63));

}


int main(void){
    nrfx_err_t status;
    (void)status;
    uint16_t combined_value;

    // Initialize Command to send to the ADC
    initialization_command();
	
    #if defined(__ZEPHYR__)
		IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_SPIM_INST_GET(SPIM_INST_IDX)), IRQ_PRIO_LOWEST,     
										NRFX_SPIM_INST_HANDLER_GET(SPIM_INST_IDX), 0, 0);
        IRQ_CONNECT(NRFX_IRQ_NUMBER_GET(NRF_TIMER_INST_GET(TIMER_INST_IDX)), IRQ_PRIO_LOWEST, 
                                        NRFX_TIMER_INST_HANDLER_GET(TIMER_INST_IDX), 0, 0);
	#endif

    LOG_INF(" <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> ");
    LOG_INF("main - TIME_TO_WAIT_US: %d [us]", TIME_TO_WAIT_US);
    LOG_INF("main - total_sampling_rate: %ld [Hz]", total_sampling_rate);
    LOG_INF("main - per_ch_SAMPLE_RATE: %ld [Hz]", per_ch_SAMPLE_RATE);

    k_sleep(K_MSEC(100));
    status = nrfx_gpiote_init(&gpiote_inst, NRFX_GPIOTE_DEFAULT_CONFIG_IRQ_PRIORITY);
    //LOG_INF("GPIOTE status: %s", nrfx_gpiote_init_check(&gpiote_inst) ? "initialized" : "not initialized");
    status = nrfx_gpiote_channel_alloc(&gpiote_inst, &out_channel);
    LOG_INF("main - tx_buf_array[0]: %p", (void*)&tx_buf_array[0][0]);
    LOG_INF("main - tx_buf_array[29]: %p", (void*)&tx_buf_array[29][0]);
    LOG_PROCESS();
    k_sleep(K_MSEC(1000));
    LOG_INF("main - tx_buf_array_2[0]: %p", (void*)&tx_buf_array_2[0][0]);
    LOG_INF("main - tx_buf_array_2[29]: %p", (void*)&tx_buf_array_2[17][0]);
    LOG_PROCESS();
    k_sleep(K_MSEC(1000));

    peripheral_setup();
    LOG_INF("main - peripheral_setup");
    nrfx_gpiote_out_task_enable(&gpiote_inst, SS_PIN_MASTER);
    nrfx_gppi_channels_enable(BIT(ppi_channel_spi_start));
    nrfx_gppi_channels_enable(BIT(ppi_channel_spi_end));
    nrfx_timer_enable(&timer0_inst);
    LOG_INF("main - nrfx_timer_enable");
	k_sem_take(&sem_condition1, K_FOREVER);
	k_sem_take(&sem_condition2, K_FOREVER);
	LOG_INF("main - k_sem_take");
	for (int t = -2; t < MAX_SPI_COUNT; t++) {
		LOG_INF(" %d >>>> rx: 0x%02x%02x", t, rx_buf_array[t+2][0], rx_buf_array[t+2][1]);  
        LOG_PROCESS();          
	}
    int32_t voltage_uV;
    int32_t voltage_whole;
    int32_t voltage_frac;
    for (int t = 30; t < MAX_SPI_COUNT; t++){
        combined_value = (uint16_t)((rx_buf_array[t][0] << 8) | rx_buf_array[t][1]);
  		voltage_uV = (int32_t)(0.195 * (combined_value - 32768));
        voltage_whole = voltage_uV / 1000;
        voltage_frac = abs(voltage_uV % 1000);
        LOG_INF(" index:%d >>> %d.%03d [mV]", t, voltage_whole, voltage_frac);
    }

	LOG_INF("spim>>  %d", spi_counter);
	LOG_INF(" MAIN END");
	return 0;

}

Related