This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Parallel to Serial Converter

Hello everyone,

I write this post because I haven't been able to find a solution to a problem I'm having to implement a parallel to serial converter. I am employing the nrf52 DK device.

My scheme of the circuit is the following one:

- Inputs :    -Clock : 2 MHz signal.

                  -Flag : 150 KHz signal.

- Outputs :  - Out : Serial output.

                   - Enable: Signal that in HIGH while Out is being transmitted.

-Internal:     - Values_8 : Array of 8 bool elements (they can change).

- What do I want to do? : Every time Flag goes low to high, I have to transmit Values_8 by Out at Clock frequency. It means, each of the 8 elements of Values_8 have to be transmitted in a Clock period. 

Firstly, I am trying to transmit the information 1 bit at 2 MHz in a serial way, without Flag. However, my main problem relies on the operation frequency: I set the input pin Clock as a GPIOTE with high priority, sensing a rising edge of this pin. When the edge is sensed, it employs its event_handler where a GPIO output is set or clear regarding the value of Value_8. However, this works only for a frequency of 100 kHz. I attach a simpler code which toggle a pin when Clock goes low to high. It only works for frequencies below 100 kHz. The event is "in_pin_handler".

void in_pin_handler(nrf_drv_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
	
  nrf_gpio_pin_toggle(28);
}
/**
 * @brief Function for configuring: PIN_IN pin for input, PIN_OUT pin for output,
 * and configures GPIOTE to give an interrupt on pin change.
 */
static void gpio_init(void)
{
	
	
		NRF_CLOCK->TASKS_HFCLKSTART = 1;
  
    nrf_drv_gpiote_init();

	
	  nrf_gpio_cfg_output(28);
	
		nrf_gpio_cfg_sense_input(22, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_SENSE_LOW);

    nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_LOTOHI(true);
    in_config.pull = NRF_GPIO_PIN_NOPULL;

    nrf_drv_gpiote_in_init(22, &in_config, in_pin_handler);
   
    nrf_drv_gpiote_in_event_enable(22, true);
}

/**
 * @brief Function for application main entry.
 */
int main(void)
{

		gpio_init();

    while (true)
    {
        // Do nothing.
    }
}


/** @} */

If I implement this employing GPIOTEs connection by PPI (This is the code) it correctly works at 2 MHz, however I don't know how to transmit the information of a bit to the output, I only can set, clear or toggle the output pin. I don't know how to implement an if statement, or something like that, in order to check the value (high or low) of the output pin.

static void led_blinking_setup()
{
    uint32_t compare_evt_addr;
    uint32_t gpiote_task_addr;
    nrf_ppi_channel_t ppi_channel;
    ret_code_t err_code;
	
   nrf_drv_gpiote_out_config_t config = GPIOTE_CONFIG_OUT_TASK_TOGGLE(false); //this has be configured regarding the value of a global variable
	  err_code = nrf_drv_gpiote_out_init(28, &config);
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_ppi_channel_alloc(&ppi_channel);
    APP_ERROR_CHECK(err_code);

	
    nrf_gpio_cfg_sense_input(22, NRF_GPIO_PIN_NOPULL, NRF_GPIO_PIN_SENSE_LOW);

    nrf_drv_gpiote_in_config_t in_config = GPIOTE_CONFIG_IN_SENSE_LOTOHI(true);
    in_config.pull = NRF_GPIO_PIN_NOPULL;

    nrf_drv_gpiote_in_init(22, &in_config, NULL);
   
    nrf_drv_gpiote_in_event_enable(22, true);
	
	
 	compare_evt_addr = nrf_drv_gpiote_in_event_addr_get(22);
    gpiote_task_addr = nrf_drv_gpiote_out_task_addr_get(28);

    err_code = nrf_drv_ppi_channel_assign(ppi_channel, compare_evt_addr, gpiote_task_addr);
    APP_ERROR_CHECK(err_code);

   err_code = nrf_drv_ppi_channel_enable(ppi_channel);
   APP_ERROR_CHECK(err_code);

 	nrf_drv_gpiote_out_task_enable(28);
		
}

int main(void)
{
		
	NRF_CLOCK->TASKS_HFCLKSTART = 1;
    ret_code_t err_code;

    err_code = nrf_drv_ppi_init();
    APP_ERROR_CHECK(err_code);

    err_code = nrf_drv_gpiote_init();
    APP_ERROR_CHECK(err_code);


	led_blinking_setup();
    while (true)
    {
			__WFI();
        // Do Nothing - GPIO can be toggled without software intervention.
    }
}

I would like to know a possible solution to achieve, or an event_handler at upper frequencies, or an alternative to set/clear a GPIO pin at high frequencies driven by an external Clock and regarding a global variable (or memory) that is changing.

Thank you so much for your help.

  • Do you mean this connection? FLAG --> GPIOTE (INPUT RISING EDGE) --> PPI --> SPI (MASTER). Because the Flag is a bit that comes from a custom IC that is High when you can write in their Serial to parallel register, and I don't have anymore outputs from the IC. 

  • Is not possible because you can't change the clock of the SPI to 2 MHz

  • I have just achieve to sent the information that I need to transfer but a maximum clock frequency of 500 kHz. This is the code that I employ. When I employ higher frequencies than 500 kHz, for example 1 MHz, the output code is correct, but the duration of the pulses is different for each value. I would like to know if there is a way to read a signal of 2 MHz by GPIO or GPIOTE and do and action related with a value of the RAM, Could I do something related with DMA? For example --> Clock --> GPIOTE --> DMA (read) --> RAM --> DMA (write) --> GPIOTE. 

    Another question that I have... what is the maximum frequency for a event_handler of GPIOTE? 

    static void gpio_init(void)
    {
    
    	
    	nrf_gpio_cfg_input(22, NRF_GPIO_PIN_NOPULL);
    
    	NRF_GPIOTE->CONFIG[0] =  (GPIOTE_CONFIG_POLARITY_LoToHi << GPIOTE_CONFIG_POLARITY_Pos)
                          | (22 << GPIOTE_CONFIG_PSEL_Pos)   
                          | (GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos);
    	
    }
    
    
    /**
     * @brief Function for application main entry.
     */
    int main(void)
    {
    	
    		NRF_CLOCK->TASKS_HFCLKSTART = 1;
    		int a=7;
    		bool b[8] = {1,0,1,0,1,0,1,0};
    		
      
        nrf_drv_gpiote_init();
    		nrf_gpio_cfg_output(28);
    
    		gpio_init();;
    
        while (true)
        { 
    			
    			
    			if(NRF_GPIOTE->EVENTS_IN[0] && a<7){
    				NRF_GPIOTE->EVENTS_IN[0] = 0;
    				if(b[a]){
    					nrf_gpio_pin_set(28);
    					a=a+1;
    				}
    				else{
    					nrf_gpio_pin_clear(28);
    					a=a+1;
    				}}
    			
    				else if(NRF_GPIOTE->EVENTS_IN[0] && a==7)
    				{ NRF_GPIOTE->EVENTS_IN[0] = 0;
    				if(b[a]){
    					nrf_gpio_pin_set(28);
    					a=0;
    				}
    				else{
    					nrf_gpio_pin_clear(28);
    					a=0;
    		}}
    
    
    
    
    
    }}

    Thank you!

  • It seems like the project with polling is the best way to go, since you achieved a higher frequency than the one using the interrupt handler. I am not sure how to solve it using PPI and EasyDMA.

    In the code where you are polling the input, try switching out nrf_gpio_pin_toggle(..) with TASK_OUT[..], which will save you some clock cycles. First you need to create two different tasks, one for setting the pin low, and one for setting it high, then you call the appropriate task depending on b[a]. Also, turn on optimization when compiling your code, in order to make it a little faster.

    norbe13 said:
    Another question that I have... what is the maximum frequency for a event_handler of GPIOTE? 

    I have not calculated this, but it depends on the amount of code running in the interrupt handler. I think the interrupt latency on entry and the interrupt latency on exit is 12+10=22 cycles. If you add those cycles to the amount of cycles in the handler + everything running in the background, and eventually divide the CPU clock frequency (64MHz for the nRF52832) with the amount of cycles, you will get the frequency of the GPIOTE handler. 

    Best regards,

    Simon

Related