[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;

}

Parents
  • Hi,

    Let's say TIME_TO_WAIT_US is 10, then after every 10us SPI transaction is being executed?

    How many bytes you are transmitting?
    How many bytes you are receiving?
    What is the frequency of operation?
    How much time it would require to tx/rx using a spim transaction?
    There will always be sometime after the CSN has been pulled low and the clock (CLK) is generated, and similarly at the end of transaction there will be time after CLK is disabled to the point when CSN is pulled back.

    So you need to see if your requirement is realistic and design / manage accordingly.

    Regards,
    Naeem

Reply
  • Hi,

    Let's say TIME_TO_WAIT_US is 10, then after every 10us SPI transaction is being executed?

    How many bytes you are transmitting?
    How many bytes you are receiving?
    What is the frequency of operation?
    How much time it would require to tx/rx using a spim transaction?
    There will always be sometime after the CSN has been pulled low and the clock (CLK) is generated, and similarly at the end of transaction there will be time after CLK is disabled to the point when CSN is pulled back.

    So you need to see if your requirement is realistic and design / manage accordingly.

    Regards,
    Naeem

Children
No Data
Related