Пример #1
0
/* Disable and reset the SPI peripheral using the procedure outlined in the
 * STM32F302 Reference Manual (section 28.5.8 pg. 815) without blocking.
 * This function assumes that a transaction isn't currently in progress. */
static void disable_and_reset_spi_properly(void){
	dma_disable_channel(DMA1, DMA_CHANNEL3);
	dma_disable_channel(DMA1, DMA_CHANNEL2);
	spi_disable(SPI3);
	rcc_periph_reset_pulse(RST_SPI3);
	DmaCleanupNeeded = false;
	TxSpi = false;
}
// for the STM32F0, interrupts for DMA_CH4 and DMA_CH5 are combined
void dma1_channel4_5_isr(void) {
    usart_disable_tx_dma(USART2);
    usart_disable_rx_dma(USART2);
    dma_clear_interrupt_flags(DMA1, DMA_CHANNEL4, DMA_TCIF);
    dma_clear_interrupt_flags(DMA1, DMA_CHANNEL5, DMA_TCIF);
    dma_disable_channel(DMA1, DMA_CHANNEL4);
    dma_disable_channel(DMA1, DMA_CHANNEL5);
}
Пример #3
0
/// Processing done after rx completes.
void process_rx_dma_interrupt(struct spi_periph *periph) {
  struct spi_periph_dma *dma = periph->init_struct;
  struct spi_transaction *trans = periph->trans[periph->trans_extract_idx];

  /* Disable DMA Channel */
  dma_disable_transfer_complete_interrupt(dma->dma, dma->rx_chan);

  /* Disable SPI Rx request */
  spi_disable_rx_dma((uint32_t)periph->reg_addr);

  /* Disable DMA rx channel */
  dma_disable_channel(dma->dma, dma->rx_chan);


  if (dma->rx_extra_dummy_dma) {
    /*
     * We are finished the first part of the receive with real data,
     * but still need to run the dummy to get a transfer complete interrupt
     * after the complete transaction is done.
     */

    /* Reset the flag so this only happens once in a transaction */
    dma->rx_extra_dummy_dma = FALSE;

    /* Use the difference in length between rx and tx */
    uint16_t len_remaining = trans->output_length - trans->input_length;

    spi_configure_dma(dma->dma, dma->rx_chan, (uint32_t)dma->spidr,
                      (uint32_t)&(dma->rx_dummy_buf), len_remaining, trans->dss, FALSE);
    dma_set_read_from_peripheral(dma->dma, dma->rx_chan);
    dma_set_priority(dma->dma, dma->rx_chan, DMA_CCR_PL_HIGH);

    /* Enable DMA transfer complete interrupts. */
    dma_enable_transfer_complete_interrupt(dma->dma, dma->rx_chan);
    /* Enable DMA channels */
    dma_enable_channel(dma->dma, dma->rx_chan);
    /* Enable SPI transfers via DMA */
    spi_enable_rx_dma((uint32_t)periph->reg_addr);
  }
  else {
    /*
     * Since the receive DMA is always run until the very end
     * and this interrupt is triggered after the last data word was read,
     * we now know that this transaction is finished.
     */

    /* Run the callback */
    trans->status = SPITransSuccess;
    if (trans->after_cb != 0) {
      trans->after_cb(trans);
    }

    /* AFTER the callback, then unselect the slave if required */
    if (trans->select == SPISelectUnselect || trans->select == SPIUnselect) {
      SpiSlaveUnselect(trans->slave_idx);
    }

    spi_next_transaction(periph);
  }
}
Пример #4
0
/** @brief DMA2,3 callback
 *
 * callback function for DMA transfer
 */
void dma1_channel2_3_isr( void )
{
	/* after DMA transfer completed enable timer 3 overflow */
	dma_clear_interrupt_flags(DMA1, DMA_CHANNEL2, DMA_TCIF);
	timer_enable_irq(TIM3, TIM_DIER_UIE);

	dma_disable_channel( DMA1, DMA_CHANNEL2);
	dma_disable_channel( DMA1, DMA_CHANNEL3);
	dma_disable_channel( DMA1, DMA_CHANNEL4);

	timer_disable_irq( TIM3, TIM_DIER_CC1DE);
	timer_disable_irq( TIM3, TIM_DIER_CC3DE);
	timer_disable_irq( TIM3, TIM_DIER_UDE);


}
Пример #5
0
// DMA1_CHANNEL2 UART3_TX
void dma1_channel2_isr(void) {
	if ((DMA1_ISR & DMA_ISR_TCIF2) != 0) {
		DMA1_IFCR |= DMA_IFCR_CTCIF2;
		dma_disable_transfer_complete_interrupt(DMA1, DMA_CHANNEL2);
		usart_disable_tx_dma(USART3);
		dma_disable_channel(DMA1, DMA_CHANNEL2);
		(*tx_done_handler)();
	}
}
Пример #6
0
void _USART_DMA_ISR(void)
{
    DMA_IFCR(_USART_DMA) |= DMA_IFCR_CTCIF(_USART_DMA_CHANNEL);

    dma_disable_transfer_complete_interrupt(_USART_DMA, _USART_DMA_CHANNEL);
    usart_disable_tx_dma(_USART);
    dma_disable_channel(_USART_DMA, _USART_DMA_CHANNEL);

    busy = 0;
}
Пример #7
0
void acq_pause()
{
	gpio_clear(BANK_LED, GPIO_LED);
	spi_disable(SPI_C1);
	spi_disable_rx_dma(SPI_C1);
	dma_disable_transfer_complete_interrupt(DMA1, DMA_CHANNEL2);
	dma_disable_half_transfer_interrupt(DMA1, DMA_CHANNEL2);
	dma_disable_channel(DMA1, DMA_CHANNEL2);
	nvic_disable_irq(NVIC_DMA1_CHANNEL2_IRQ);
}
Пример #8
0
/* Receive data using DMA. */
void rx_spi(void *data, uint16_t size){
	if(DmaCleanupNeeded)cleanup_dma_spi();

	/* This byte is sent continuously when rx_spi() receives data. */
	static const uint8_t RxSpiDummyByte = 0x00;

	spi_enable_rx_dma(SPI3);
	
	dma_channel_reset(DMA1, DMA_CHANNEL2);
	dma_disable_channel(DMA1, DMA_CHANNEL2);
	dma_set_peripheral_address(DMA1, DMA_CHANNEL2, (uint32_t) &(SPI_DR(SPI3)));
	dma_set_memory_address(DMA1, DMA_CHANNEL2, (uint32_t) data);
	dma_set_number_of_data(DMA1, DMA_CHANNEL2, size);
	dma_set_priority(DMA1, DMA_CHANNEL2, DMA_CCR_PL_HIGH);
	dma_set_memory_size(DMA1, DMA_CHANNEL2, DMA_CCR_MSIZE_8BIT);
	dma_set_peripheral_size(DMA1, DMA_CHANNEL2, DMA_CCR_PSIZE_8BIT);
	dma_enable_memory_increment_mode(DMA1, DMA_CHANNEL2);
	dma_disable_peripheral_increment_mode(DMA1, DMA_CHANNEL2);
	dma_set_read_from_peripheral(DMA1, DMA_CHANNEL2);
	dma_enable_channel(DMA1, DMA_CHANNEL2);

	/* The SPI peripheral is driven by transmitted bytes, so dummy bytes
	 * must be sent in order to receive data. This is accomplished with DMA
	 * by simply not incrementing the memory address. */
	dma_channel_reset(DMA1, DMA_CHANNEL3);
	dma_disable_channel(DMA1, DMA_CHANNEL3);
	dma_set_peripheral_address(DMA1, DMA_CHANNEL3, (uint32_t) &(SPI_DR(SPI3)));
	dma_set_memory_address(DMA1, DMA_CHANNEL3, (uint32_t) &RxSpiDummyByte);
	dma_set_number_of_data(DMA1, DMA_CHANNEL3, size);
	dma_set_priority(DMA1, DMA_CHANNEL3, DMA_CCR_PL_HIGH);
	dma_set_memory_size(DMA1, DMA_CHANNEL3, DMA_CCR_MSIZE_8BIT);
	dma_set_peripheral_size(DMA1, DMA_CHANNEL3, DMA_CCR_PSIZE_8BIT);
	dma_disable_memory_increment_mode(DMA1, DMA_CHANNEL3);
	dma_disable_peripheral_increment_mode(DMA1, DMA_CHANNEL3);
	dma_set_read_from_memory(DMA1, DMA_CHANNEL3);
	dma_enable_channel(DMA1, DMA_CHANNEL3);
	
	spi_enable_tx_dma(SPI3);
	spi_enable(SPI3);
	DmaCleanupNeeded = true;
}
Пример #9
0
/* Cleanup between DMA transfers. */
static void cleanup_dma_spi(void){
	/* Disable SPI without resetting the peripheral. */
	dma_disable_channel(DMA1, DMA_CHANNEL3);
	dma_disable_channel(DMA1, DMA_CHANNEL2);
	spi_disable(SPI3);
	if(TxSpi){
		/* After tx_spi() completes there will be several nonsense bytes
		 * in the RXFIFO. They must be cleared out so that they don't
		 * corrupt a subsequent SPI read. */
		uint8_t throwaway;
		throwaway = SPI_DR(SPI3);
		throwaway = SPI_DR(SPI3);
		throwaway = SPI_DR(SPI3);
		throwaway = SPI_DR(SPI3);
		throwaway = throwaway; /* Suppress compiler warnings. */
		TxSpi = false;
	}
	spi_disable_tx_dma(SPI3);
	spi_disable_rx_dma(SPI3);
	DmaCleanupNeeded = false;
}
Пример #10
0
// DMA1_CHANNEL2 UART3_RX
void dma1_channel3_isr(void){
	if ((DMA1_ISR & DMA_ISR_TCIF3) != 0) {
		DMA1_IFCR |= DMA_IFCR_CTCIF3;
		dma_disable_transfer_complete_interrupt(DMA1, DMA_CHANNEL3);
		usart_disable_rx_dma(USART3);
		dma_disable_channel(DMA1, DMA_CHANNEL3);

		gpio_set(GPIOB, GPIO_DEBUG_1);
		// hal_uart_manual_rts_set();
		(*rx_done_handler)();
	}
}
Пример #11
0
/* Simultaneously transmit and receive data using DMA. */
void rxtx_spi(void *rxdata, const void *txdata, uint16_t size){
	if(DmaCleanupNeeded)cleanup_dma_spi();

	spi_enable_rx_dma(SPI3);
	
	dma_channel_reset(DMA1, DMA_CHANNEL2);
	dma_disable_channel(DMA1, DMA_CHANNEL2);
	dma_set_peripheral_address(DMA1, DMA_CHANNEL2, (uint32_t) &(SPI_DR(SPI3)));
	dma_set_memory_address(DMA1, DMA_CHANNEL2, (uint32_t) rxdata);
	dma_set_number_of_data(DMA1, DMA_CHANNEL2, size);
	dma_set_priority(DMA1, DMA_CHANNEL2, DMA_CCR_PL_HIGH);
	dma_set_memory_size(DMA1, DMA_CHANNEL2, DMA_CCR_MSIZE_8BIT);
	dma_set_peripheral_size(DMA1, DMA_CHANNEL2, DMA_CCR_PSIZE_8BIT);
	dma_enable_memory_increment_mode(DMA1, DMA_CHANNEL2);
	dma_disable_peripheral_increment_mode(DMA1, DMA_CHANNEL2);
	dma_set_read_from_peripheral(DMA1, DMA_CHANNEL2);
	dma_enable_channel(DMA1, DMA_CHANNEL2);

	dma_channel_reset(DMA1, DMA_CHANNEL3);
	dma_disable_channel(DMA1, DMA_CHANNEL3);
	dma_set_peripheral_address(DMA1, DMA_CHANNEL3, (uint32_t) &(SPI_DR(SPI3)));
	dma_set_memory_address(DMA1, DMA_CHANNEL3, (uint32_t) txdata);
	dma_set_number_of_data(DMA1, DMA_CHANNEL3, size);
	dma_set_priority(DMA1, DMA_CHANNEL3, DMA_CCR_PL_HIGH);
	dma_set_memory_size(DMA1, DMA_CHANNEL3, DMA_CCR_MSIZE_8BIT);
	dma_set_peripheral_size(DMA1, DMA_CHANNEL3, DMA_CCR_PSIZE_8BIT);
	dma_enable_memory_increment_mode(DMA1, DMA_CHANNEL3);
	dma_disable_peripheral_increment_mode(DMA1, DMA_CHANNEL3);
	dma_set_read_from_memory(DMA1, DMA_CHANNEL3);
	dma_enable_channel(DMA1, DMA_CHANNEL3);
	
	spi_enable_tx_dma(SPI3);
	spi_enable(SPI3);

	DmaCleanupNeeded = true;
}
Пример #12
0
/* SPI receive completed with DMA */
void dma1_channel2_isr(void)
{
	gpio_set(GPIOA,GPIO4);
	if ((DMA1_ISR &DMA_ISR_TCIF2) != 0) {
		DMA1_IFCR |= DMA_IFCR_CTCIF2;
	}

	dma_disable_transfer_complete_interrupt(DMA1, DMA_CHANNEL2);

	spi_disable_rx_dma(SPI1);

	dma_disable_channel(DMA1, DMA_CHANNEL2);

	/* Increment the status to indicate one of the transfers is complete */
	transceive_status++;
	gpio_clear(GPIOA,GPIO4);
}
Пример #13
0
/* SPI transmit completed with DMA */
void dma1_channel3_isr(void)
{
	gpio_set(GPIOB,GPIO1);
	if ((DMA1_ISR &DMA_ISR_TCIF3) != 0) {
		DMA1_IFCR |= DMA_IFCR_CTCIF3;
	}

	dma_disable_transfer_complete_interrupt(DMA1, DMA_CHANNEL3);

	spi_disable_tx_dma(SPI1);

	dma_disable_channel(DMA1, DMA_CHANNEL3);

	/* If tx_len < rx_len, create a dummy transfer to clock in the remaining
	 * rx data
	 */
	if (rx_buf_remainder > 0) {
		dma_channel_reset(DMA1, DMA_CHANNEL3);
		dma_set_peripheral_address(DMA1, DMA_CHANNEL3, (uint32_t)&SPI1_DR);
		dma_set_memory_address(DMA1, DMA_CHANNEL3, (uint32_t)(&dummy_tx_buf)); // Change here
		dma_set_number_of_data(DMA1, DMA_CHANNEL3, rx_buf_remainder); // Change here
		dma_set_read_from_memory(DMA1, DMA_CHANNEL3);
		dma_disable_memory_increment_mode(DMA1, DMA_CHANNEL3); // Change here
#if USE_16BIT_TRANSFERS
		dma_set_peripheral_size(DMA1, DMA_CHANNEL3, DMA_CCR_PSIZE_16BIT);
		dma_set_memory_size(DMA1, DMA_CHANNEL3, DMA_CCR_MSIZE_16BIT);
#else
		dma_set_peripheral_size(DMA1, DMA_CHANNEL3, DMA_CCR_PSIZE_8BIT);
		dma_set_memory_size(DMA1, DMA_CHANNEL3, DMA_CCR_MSIZE_8BIT);
#endif
		dma_set_priority(DMA1, DMA_CHANNEL3, DMA_CCR_PL_HIGH);

		rx_buf_remainder = 0; // Clear the buffer remainder to disable this section later

		dma_enable_transfer_complete_interrupt(DMA1, DMA_CHANNEL3);
		dma_enable_channel(DMA1, DMA_CHANNEL3);
		spi_enable_tx_dma(SPI1);
	} else {
		/* Increment the status to indicate one of the transfers is complete */
		transceive_status++;
	}

	gpio_clear(GPIOB,GPIO1);
}
void dma_enable_channel(int channel, struct dma_request *request)
{
    struct dma_channel_regs *regs = dma_regs [channel];
     
    /* TODO - transfer sizes (assumes word) */
    
    if (DMA_GET_SRC(request->source_map, channel) == DMA_INVALID)
        panicf ("DMA: invalid channel");
    
    /* setup a transfer on specified channel */
    dma_disable_channel (channel);
    
    if((unsigned long)request->source_addr < UNCACHED_BASE_ADDR)
        regs->disrc = (unsigned long)request->source_addr + UNCACHED_BASE_ADDR;
    else
        regs->disrc = (unsigned long)request->source_addr;
    regs->disrcc = request->source_control;
    
    if((unsigned long)request->dest_addr < UNCACHED_BASE_ADDR)
        regs->didst = (unsigned long)request->dest_addr + UNCACHED_BASE_ADDR;
    else
        regs->didst = (unsigned long)request->dest_addr;
    regs->didstc = request->dest_control;
    
    regs->dcon = request->control | request->count | 
                 DMA_GET_SRC(request->source_map, channel) * DCON_HWSRCSEL;

    dma_state [channel].callback = request->callback;
            
    /* Activate the channel */
    invalidate_dcache_range((void *)request->dest_addr, request->count * 4);
    
    dma_state [channel].status |= STATUS_CHANNEL_ACTIVE;
    regs->dmasktrig = DMASKTRIG_ON;

    if ((request->control & DCON_HW_SEL) == 0)
    {
        /* Start DMA */
        regs->dmasktrig |= DMASKTRIG_SW_TRIG;
    }
        
}
void pcm_play_dma_stop(void)
{
    is_playing = false;

    dma_disable_channel(0);

    /* Ensure byte counts read back 0 */
    DMAC_CH_SRC_ADDR(0) = 0;
    dma_start_addr = NULL;
    dma_start_size = 0;
    dma_rem_size = 0;

    dma_release();

#ifdef HAVE_RECORDING
    if (!is_recording)
        bitclr32(&CGU_AUDIO, (1<<11));
#endif

    play_callback_pending = false;
}
Пример #16
0
/// Processing done after tx completes
void process_tx_dma_interrupt(struct spi_periph *periph) {
  struct spi_periph_dma *dma = periph->init_struct;
  struct spi_transaction *trans = periph->trans[periph->trans_extract_idx];

  /* Disable DMA Channel */
  dma_disable_transfer_complete_interrupt(dma->dma, dma->tx_chan);

  /* Disable SPI TX request */
  spi_disable_tx_dma((uint32_t)periph->reg_addr);

  /* Disable DMA tx channel */
  dma_disable_channel(dma->dma, dma->tx_chan);

  if (dma->tx_extra_dummy_dma) {
    /*
     * We are finished the first part of the transmit with real data,
     * but still need to clock in the rest of the receive data.
     * Set up a dummy dma transmit transfer to accomplish this.
     */

    /* Reset the flag so this only happens once in a transaction */
    dma->tx_extra_dummy_dma = FALSE;

    /* Use the difference in length between tx and rx */
    uint16_t len_remaining = trans->input_length - trans->output_length;

    spi_configure_dma(dma->dma, dma->tx_chan, (uint32_t)dma->spidr,
                      (uint32_t)&(dma->tx_dummy_buf), len_remaining, trans->dss, FALSE);
    dma_set_read_from_memory(dma->dma, dma->tx_chan);
    dma_set_priority(dma->dma, dma->tx_chan, DMA_CCR_PL_MEDIUM);

    /* Enable DMA transfer complete interrupts. */
    dma_enable_transfer_complete_interrupt(dma->dma, dma->tx_chan);
    /* Enable DMA channels */
    dma_enable_channel(dma->dma, dma->tx_chan);
    /* Enable SPI transfers via DMA */
    spi_enable_tx_dma((uint32_t)periph->reg_addr);

  }
}
Пример #17
0
void acq_init()
{
	/* Initialize amplifier control GPIO */
	gpio_mode_setup(BANK_AMP, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_AMP);
	gpio_clear(BANK_AMP, GPIO_AMP);
	gpio_mode_setup(BANK_LED, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO_LED);
	gpio_clear(BANK_LED, GPIO_LED);

	/* Initialize SPI GPIOs */
	gpio_mode_setup(BANK_CS, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_CS);
	gpio_mode_setup(BANK_SCLK, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_SCLK);
	gpio_mode_setup(BANK_DOUT, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_DOUT);

	gpio_set_af(BANK_CS, GPIO_AF6, GPIO_CS);
	gpio_set_af(BANK_SCLK, GPIO_AF6, GPIO_SCLK);
	gpio_set_af(BANK_DOUT, GPIO_AF6, GPIO_DOUT);

	rcc_periph_clock_enable(RCC_SPI3);

	acq_spi_init(SPI_C1);

	rcc_periph_clock_enable(RCC_DMA1);
	dma_channel_reset(DMA1, DMA_CHANNEL2);
	dma_disable_channel(DMA1, DMA_CHANNEL2);

	dma_set_peripheral_address(DMA1, DMA_CHANNEL2, (uint32_t)&SPI_DR(SPI_C1));
	dma_set_memory_address(DMA1, DMA_CHANNEL2, (uint32_t)&acq_channel.buff);
	dma_set_number_of_data(DMA1, DMA_CHANNEL2, BUFFER_SIZE);

	dma_set_priority(DMA1, DMA_CHANNEL2, DMA_CCR_PL_MEDIUM);

	dma_set_memory_size(DMA1, DMA_CHANNEL2, DMA_CCR_MSIZE_16BIT);
	dma_set_peripheral_size(DMA1, DMA_CHANNEL2, DMA_CCR_PSIZE_16BIT);
	dma_enable_memory_increment_mode(DMA1, DMA_CHANNEL2);
	dma_set_read_from_peripheral(DMA1, DMA_CHANNEL2);
	dma_enable_circular_mode(DMA1, DMA_CHANNEL2);

	nvic_set_priority(NVIC_DMA1_CHANNEL2_IRQ, PRIO_ACQ);
}
static void ResetDMA()
{
   dma_disable_channel(DMA1, TERM_USART_DMACHAN);
   dma_set_number_of_data(DMA1, TERM_USART_DMACHAN, TERM_BUFSIZE);
   dma_enable_channel(DMA1, TERM_USART_DMACHAN);
}
Пример #19
0
int main(void)
{
	/* Exactly 20 bytes including '0' at the end.
	   We want to transfer 32bit * 5 so it should fit */
	char s1[20] = "Hello STM MEM2MEM\r\n";
	char s2[20];

        rcc_clock_setup_in_hse_16mhz_out_72mhz();
	gpio_setup();
	usart_setup();

	gpio_clear(GPIOB, GPIO7);	/* LED1 on */
	gpio_set(GPIOB, GPIO6);		/* LED2 off */

	my_usart_print_string(USART1, "s1 ");
	my_usart_print_string(USART1, s1);

	rcc_peripheral_enable_clock(&RCC_AHBENR, RCC_AHBENR_DMA1EN);

	/* MEM2MEM mode for channel 1. */
	dma_enable_mem2mem_mode(DMA1, DMA_CHANNEL1);

	/* Highest priority. */
	dma_set_priority(DMA1, DMA_CHANNEL1, DMA_CCR1_PL_VERY_HIGH);

	/* 32Bit wide transfer for source and destination. */
	dma_set_memory_size(DMA1, DMA_CHANNEL1, DMA_CCR1_MSIZE_32BIT);
	dma_set_peripheral_size(DMA1, DMA_CHANNEL1, DMA_CCR1_PSIZE_32BIT);

	/* After each 32Bit we have to increase the addres because we use RAM. */
	dma_enable_memory_increment_mode(DMA1, DMA_CHANNEL1);
	dma_enable_peripheral_increment_mode(DMA1, DMA_CHANNEL1);

	/* We define the source as peripheral. */
	dma_set_read_from_peripheral(DMA1, DMA_CHANNEL1);

	/* We want to transfer string s1. */
	dma_set_peripheral_address(DMA1, DMA_CHANNEL1, (u32) &s1);

	/* Destination should be string s2. */
	dma_set_memory_address(DMA1, DMA_CHANNEL1, (u32) &s2);

	/* Set number of DATA to transfer.
	   Remember that this means not necessary bytes but MSIZE or PSIZE
	   depending from your source device. */
	dma_set_number_of_data(DMA1, DMA_CHANNEL1, 5); 

	/* Start DMA transfer. */
	dma_enable_channel(DMA1, DMA_CHANNEL1);

	/* TODO: write a function to get the interrupt flags. */
	while(!(DMA_ISR(DMA1) & 0x0000001))
	{
	}

	dma_disable_channel(DMA1, DMA_CHANNEL1);

	/* String s1 should now already be transferred to s2. */
	my_usart_print_string(USART1, "s2 ");
	my_usart_print_string(USART1, s2);

	gpio_clear(GPIOB, GPIO6);	/* LED2 on */
	while(1); /* Halt. */

	return 0;
}