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.

Parents
  • Thank you all for your help! 

    Finally I achieved that I wanted but only for 1 MHZ. I attach the code. I have to say that now (in the code is included) I need that Out has to be Out1 and Out2 with the same value but for two output pins. If somebody find a way to optimize the code, please tell me! 

    static void GPIO_TIMER_Init(void)
    {  
    
    		
    		nrf_gpio_cfg_output(28); //Out1
    		nrf_gpio_cfg_output(29); //Out2
    		nrf_gpio_cfg_output(30); //Enable
    		
    
    	
    	//GPIOTE FOR FLAG SIGNAL
    						nrf_gpio_cfg_input(17, NRF_GPIO_PIN_NOPULL); // Enable pullup on the GPIO
    						NRF_GPIOTE->CONFIG[0] = (GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos) | 
    																		(GPIOTE_CONFIG_POLARITY_HiToLo << GPIOTE_CONFIG_POLARITY_Pos) | 
    																		(17 << GPIOTE_CONFIG_PSEL_Pos);
    	//GPIOTE FOR CLOCK SIGNAL
    						nrf_gpio_cfg_input(22, NRF_GPIO_PIN_NOPULL); // Enable pullup on the GPIO
    						NRF_GPIOTE->CONFIG[1] = (GPIOTE_CONFIG_MODE_Event << GPIOTE_CONFIG_MODE_Pos) | 
    																		(GPIOTE_CONFIG_POLARITY_LoToHi << GPIOTE_CONFIG_POLARITY_Pos) | 
    																		(22 << GPIOTE_CONFIG_PSEL_Pos); 
    
    														
    														
    	NVIC_DisableIRQ(TIMER2_IRQn);
      NVIC_ClearPendingIRQ(TIMER2_IRQn);
    
    //TIMER FOR CLOCK 
    
      NRF_TIMER2->MODE = TIMER_MODE_MODE_Counter;              // Set the timer in Counter Mode
    	NRF_TIMER2->SHORTS = TIMER_SHORTS_COMPARE0_CLEAR_Enabled << TIMER_SHORTS_COMPARE0_CLEAR_Pos;
      NRF_TIMER2->TASKS_CLEAR = 1;                             // clear the task first to be usable for later
    	NRF_TIMER2->BITMODE = TIMER_BITMODE_BITMODE_08Bit;		   //Set counter to 16 bit resolution
    	NRF_TIMER2->CC[0] = 1;                               //Set value for TIMER2 compare register 0
    	
    
     NRF_TIMER2->INTENSET = (TIMER_INTENSET_COMPARE0_Enabled << TIMER_INTENSET_COMPARE0_Pos);
     NVIC_EnableIRQ(TIMER2_IRQn);
      
      //TIMER FOR ENABLE
    	
    	NRF_TIMER1->MODE = TIMER_MODE_MODE_Counter;              // Set the timer in Counter Mode
    	NRF_TIMER1->SHORTS = (TIMER_SHORTS_COMPARE1_CLEAR_Enabled << TIMER_SHORTS_COMPARE1_CLEAR_Pos) |
    											 (TIMER_SHORTS_COMPARE1_STOP_Enabled << TIMER_SHORTS_COMPARE1_STOP_Pos);
      NRF_TIMER1->TASKS_CLEAR = 1;                             // clear the task first to be usable for later
    	NRF_TIMER1->BITMODE = TIMER_BITMODE_BITMODE_08Bit;		   //Set counter to 16 bit resolution
    	NRF_TIMER1->CC[1] = 8;                               //Set value for TIMER2 compare register 1 
    
    
    	NRF_PPI->CH[0].TEP = (uint32_t)&NRF_TIMER2->TASKS_START; //Start TIMER2
    	NRF_PPI->CH[0].EEP = (uint32_t)&NRF_GPIOTE->EVENTS_IN[0];
    	NRF_PPI->CHEN |= PPI_CHEN_CH0_Enabled << PPI_CHEN_CH0_Pos;
    
    	NRF_PPI->CH[1].TEP = (uint32_t)&NRF_TIMER1->TASKS_START; //START TIMER1
    	NRF_PPI->CH[1].EEP = (uint32_t)&NRF_GPIOTE->EVENTS_IN[0];
    	NRF_PPI->CHEN |= PPI_CHEN_CH1_Enabled << PPI_CHEN_CH1_Pos;
    
    	NRF_PPI->CH[2].TEP = (uint32_t)&NRF_TIMER2->TASKS_COUNT; //COUNT TIMER2
    	NRF_PPI->CH[2].EEP = (uint32_t)&NRF_GPIOTE->EVENTS_IN[1];
    	NRF_PPI->CHEN |= PPI_CHEN_CH2_Enabled << PPI_CHEN_CH2_Pos;
    	
    	NRF_PPI->CH[3].TEP = (uint32_t)&NRF_TIMER1->TASKS_COUNT; //COUNT TIMER1
    	NRF_PPI->CH[3].EEP = (uint32_t)&NRF_GPIOTE->EVENTS_IN[1];
    	NRF_PPI->CHEN |= PPI_CHEN_CH3_Enabled << PPI_CHEN_CH3_Pos;
    	
    	
    	NRF_PPI->CH[6].TEP = (uint32_t)&NRF_TIMER2->TASKS_STOP; //STOP TIMER2
      NRF_PPI->CH[6].EEP = (uint32_t)&NRF_TIMER1->EVENTS_COMPARE[1];
      NRF_PPI->CHEN |= PPI_CHEN_CH6_Enabled << PPI_CHEN_CH6_Pos;
    	
    
    	
    }
    
    void TIMER2_IRQHandler(void)
    {
    		NRF_TIMER2->EVENTS_COMPARE[0] = 0;	       //Clear compare register 0 event
    	
    		
    	
    	if(lect<7)
        {
    			
    				if(b[lect]){
    					NRF_GPIO->OUTSET = (1UL << 30);
    					NRF_GPIO->OUTSET = (1UL << 28) | (1UL << 29);  
    					
    				}
    				else{
    					NRF_GPIO->OUTSET = (1UL << 30);
    					NRF_GPIO->OUTCLR = (1UL << 28) | (1UL << 29);
    					
    				}
    				lect++;
    				}
    				else{
    					if(b[lect]){
    					NRF_GPIO->OUTSET = (1UL << 30);
    					NRF_GPIO->OUTSET = (1UL << 28) | (1UL << 29);  
    					
    				}
    				else{
    					NRF_GPIO->OUTSET = (1UL << 30);
    					NRF_GPIO->OUTCLR = (1UL << 28) | (1UL << 29);
    					}
    				lect=0;
        }
        
    }
    
     int main(void)
    {
    	
    	// VARIABLE INITIALIZATION 
          lect=0;
    			NRF_GPIO->OUTCLR = (1UL << 30);
    			
    	
    	 // WAIT FOR HFCLK START
    			NRF_CLOCK->TASKS_HFCLKSTART = 1;
    			while(NRF_CLOCK->EVENTS_HFCLKSTARTED != 1){}
    				
    			GPIO_TIMER_Init();
    
            for (;;)
        {
    				
    				NRF_GPIOTE->EVENTS_IN[0]=0;
    				
    				
            if(NRF_TIMER1->EVENTS_COMPARE[1]){
    				lect=0;
    				NRF_GPIO->OUTCLR = (1UL << 30);}
    				while(NRF_GPIOTE->EVENTS_IN[0] != 1){}
    					
    				}
    		
    		}
        }
    
    

  • Why don't you set the output pins in the for (;;) function in main()? Using the interrupt handler requires some extra cycles as explained earlier. Regarding the question about Out1 and Out2, you could take a look at the fork mechanims of PPI.

    I simplified the IRQHandler, but I don't think it will make much difference regarding speed:

    void optimized_TIMER2_IRQHandler(void)
    {
    	NRF_TIMER2->EVENTS_COMPARE[0] = 0;	       //Clear compare register 0 event
    	NRF_GPIO->OUTSET = (1UL << 30);
    	
    	if(b[lect]){
    		NRF_GPIO->OUTSET = (1UL << 28) | (1UL << 29);  
    	}else{
    		NRF_GPIO->OUTCLR = (1UL << 28) | (1UL << 29);	
    	}
    	if(lect<7){
    		lect++;
    	}else{
    		lect=0;
    	}
    }

    Best regards,

    Simon

Reply
  • Why don't you set the output pins in the for (;;) function in main()? Using the interrupt handler requires some extra cycles as explained earlier. Regarding the question about Out1 and Out2, you could take a look at the fork mechanims of PPI.

    I simplified the IRQHandler, but I don't think it will make much difference regarding speed:

    void optimized_TIMER2_IRQHandler(void)
    {
    	NRF_TIMER2->EVENTS_COMPARE[0] = 0;	       //Clear compare register 0 event
    	NRF_GPIO->OUTSET = (1UL << 30);
    	
    	if(b[lect]){
    		NRF_GPIO->OUTSET = (1UL << 28) | (1UL << 29);  
    	}else{
    		NRF_GPIO->OUTCLR = (1UL << 28) | (1UL << 29);	
    	}
    	if(lect<7){
    		lect++;
    	}else{
    		lect=0;
    	}
    }

    Best regards,

    Simon

Children
No Data
Related