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

Infrared send driver time length is not correct sometimes, why ?

I use example ble_app_uart, SoftDevice sdk 6.2.1, when receive data from bluetooth, then drive IO pin 18 to send infrared wave. received data is Infrared raw formate, which means IO output pin send wave time length (micro second), the odd data is 'mark' time length, the even data is 'space' time length. receive data is:

Blockquote

8976 4556 600 1696 612 1688 608 540
604 544 608 540 604 544 600 1700
608 1688 608 544 600 544 608 540
604 1696 600 1700 600 548 604 544
600 1700 608 544 608 540 604 544
612

Blockquote

I call sendRaw function below to send infrared wave,The forty one data length is 612 us,however, I capture the wave time length by logic analyzer, result is 1380 us, other data is correct,why this happed, and how to fix it ? Or is there any other infrared send driver ? Thank you ! image description

below is the driver using time 1 and timer 2(time 1 to generate carrier, timer 2 to control time length)

     static   void timer_delay(volatile uint32_t usecond){
    	 //delay for usecond
    
        NRF_TIMER2->TASKS_CLEAR = 1;
        NRF_TIMER2->PRESCALER =7;                  
        NRF_TIMER2->MODE      = TIMER_MODE_MODE_Timer;
        NRF_TIMER2->BITMODE   = TIMER_BITMODE_BITMODE_16Bit;
    	NRF_TIMER2->CC[0]     = (usecond>>3);
        NRF_TIMER2->TASKS_START=1;
    		
        while(NRF_TIMER2->EVENTS_COMPARE[0] == 0)
    	  {
    	   //wait 
    	  }	
    	  NRF_TIMER2->EVENTS_COMPARE[0]=0;
    
    	  NRF_TIMER2->TASKS_SHUTDOWN=1;
    }
    
   static  void Mark(uint32_t time){

      // send carrier

        nrf_gpio_cfg_output(GPIO_OUTPUT_PIN_NUMBER);
    	nrf_gpiote_task_config(3, GPIO_OUTPUT_PIN_NUMBER, \
                               NRF_GPIOTE_POLARITY_TOGGLE, GPIOTE_CONFIG_OUTINIT_Low);
       
    	  // Clear TIMER1
        NRF_TIMER1->TASKS_CLEAR = 1;
        NRF_TIMER1->PRESCALER =4;
        NRF_TIMER1->CC[0]     =26;    
        NRF_TIMER1->CC[1]     = 9; 
        NRF_TIMER1->MODE      = TIMER_MODE_MODE_Timer;
        NRF_TIMER1->BITMODE   = TIMER_BITMODE_BITMODE_16Bit;
        NRF_TIMER1->SHORTS    = (TIMER_SHORTS_COMPARE0_CLEAR_Enabled <<    TIMER_SHORTS_COMPARE0_CLEAR_Pos);
    	NRF_TIMER1->TASKS_START = 1;
    	timer_delay(time);
    	
    	  NRF_TIMER1->TASKS_SHUTDOWN = 1;
    	  NRF_GPIOTE->CONFIG[3] = (GPIOTE_CONFIG_MODE_Disabled   << GPIOTE_CONFIG_MODE_Pos);
       	NRF_GPIO->OUTCLR = (1UL << GPIO_OUTPUT_PIN_NUMBER);
    }
    
      


      static void sendRaw(volatile uint16_t buf[], int len, int hz)
    {
       // send driver
    
      for (int i = 0; i < len; i++) {
        if (i &1) {
            timer_delay(buf[i]);
        } 
        else {
            Mark(buf[i]);
        }
      }
    }

    void gpiote_init_Midea(){ 
        // init gpiote 
    		nrf_gpio_cfg_output(GPIO_OUTPUT_PIN_NUMBER);
    
    		nrf_gpiote_task_config(3, GPIO_OUTPUT_PIN_NUMBER, \
                               NRF_GPIOTE_POLARITY_TOGGLE, GPIOTE_CONFIG_OUTINIT_Low);
    		
    }

    void ppi_init_Midea(){
         //init ppi
		ppi_enable_channel(4,&(NRF_TIMER1->EVENTS_COMPARE[0]),&(NRF_GPIOTE->TASKS_OUT[3]));
		ppi_enable_channel(5,&(NRF_TIMER1->EVENTS_COMPARE[1]),&(NRF_GPIOTE->TASKS_OUT[3]));
	}

        static void ppi_enable_channel(uint32_t ch_num, volatile uint32_t *event_ptr, volatile uint32_t *task_ptr)
    {
       uint32_t err_code;
       uint32_t error;
      err_code =  sd_ppi_channel_assign(ch_num, event_ptr, task_ptr);
    	 if( err_code != NRF_SUCCESS ){
    					error = err_code;
    				}
      err_code =  sd_ppi_channel_enable_set(1 << ch_num);
    				if( err_code != NRF_SUCCESS ){
    					error = err_code;
    				}				
    }
Parents
  • Hi

    The problem is that you are being interrupted by the BLE stack in your while loop (when waiting for the TIMER2 event). In general it is not a good idea to base the system on while loops like this, it would be better to use the PPI to have TIMER2 turn on and off the carrier (TIMER1) in the background, so that BLE interrupts will not affect you.

    I decided to make a small example of my own to demonstrate this:
    ble_app_uart_IR.zip

    I chose to use a slightly different approach. Instead of using TIMER2 as a timer, I use it as a counter, and count the number of pulses from the carrier before turning it on and off.

    The API is comparable to yours I believe. You just provide the library a list of 16-bit values, each storing the number of microseconds the carrier should be on and off. To implement specific IR protocols you have to fill in the buffer correctly.

    The example is based off of ble_app_uart, and will output an IR version of the first UART byte sent to the nRF51 on P0.04
    The GPIO used is configured when calling the init function. All other parameters are configured in the ir_lib.h file (such as the frequency of the carrier).

    Best regards
    Torbjørn

  • Thanks for the example code.  It helps our project tremendously.

    As a followup to your example project, we are trying stop all gpiote, timers and PPIs after we sent the IR signal.  We do such cleanup inside the interrupt function IR_CARRIER_COUNTER_IRQHandler() when m_bits_remaining==0.  With PPIs, the code is crashing in some assembly code whenever I try to call either of the following functions:

        sd_ppi_group_task_disable(IR_PPI_GROUP);
        sd_ppi_channel_enable_clr(1 << IR_PPI_CH_A | 1 << IR_PPI_CH_B | 1 << IR_PPI_CH_C | 1 << IR_PPI_CH_D | 1 << IR_PPI_CH_E);   

    Is it because I can't do this inside a IRQ?   How can we solve this?

Reply
  • Thanks for the example code.  It helps our project tremendously.

    As a followup to your example project, we are trying stop all gpiote, timers and PPIs after we sent the IR signal.  We do such cleanup inside the interrupt function IR_CARRIER_COUNTER_IRQHandler() when m_bits_remaining==0.  With PPIs, the code is crashing in some assembly code whenever I try to call either of the following functions:

        sd_ppi_group_task_disable(IR_PPI_GROUP);
        sd_ppi_channel_enable_clr(1 << IR_PPI_CH_A | 1 << IR_PPI_CH_B | 1 << IR_PPI_CH_C | 1 << IR_PPI_CH_D | 1 << IR_PPI_CH_E);   

    Is it because I can't do this inside a IRQ?   How can we solve this?

Children
  • Hi 

    It sounds like a interrupt priority issue. 

    On the nRF52 series you have to be in IRQ priority 6 or 7 to be able to call into the SoftDevice, any higher priority (lower number) and you will get an assert. 

    Alternatively you would have to defer the SoftDevice calls to the main context, by setting a flag in the interrupt that you later check in your main loop, or by using the app_scheduler module. 

    If you have more questions on this please open a new devzone ticket, the system is not really set up to handle multiple different questions in each case. You can refer to this case and mention my name if you want, and it will assigned to me if I'm available. 

    Best regards
    Torbjørn

  • I think you are correct that it is an IRQ priority issue.  Just quickly tried using a flag in the main loop, the code runs fine without crashing.  It looks kind of ugly though to have things in separate c files.  I will try to app_scheduler way later.

    Anyway, Thanks!

  • Hi

    Good to hear that you found the issue Slight smile

    One thing worth noting is that the PPI controller is actually not protected by the SoftDevice for direct access. This means it is possible to bypass the sd_ppi API and simply access the hardware registers in the NRF_PPI module directly. 

    Then there is no limit on what interrupt level you have to be in, and you can easily enable or disable groups from anywhere in the code. 

    The only thing to keep in mind when using the NRF_PPI registers is to stay away from PPI channels used by the SoftDevice, since there is nothing stopping you from interfering with the SoftDevice PPI configuration (PPI channels 16 and up). 

    To disable a PPI group using hardware registers simply run the following code line:

    NRF_PPI->TASKS_CHG[IR_PPI_GROUP].DIS = 1;

    And enable: 

    NRF_PPI->TASKS_CHG[IR_PPI_GROUP].EN = 1;

    For more information about the PPI hardware please refer to the PPI chapter in the product specification. 

    Best regards
    Torbjørn

Related