NRF52832 UART接收不定长数据

问题:

在使用NRF52832做串口透传的时候,示例程序里UARTE,接收长度为1个字节。如果串口接收到长数据(测试了1K),则很容易导致蓝牙发送的超时重启。

解决方案:使用PPI功能,使串口具备接收不定长数据的能力。

方案参考的是Nordic的libuarte工程,但是我们这里面有soft device,其本身会占用一些外设,比如RTC0,TIMER0等。那我们就只能选择其他外设,这里选用TIMER1和TIMER2。

PPI的配置之后的接收流程如下:

  1. 设置接收缓冲区,缓冲区的长度为UARTE能接受的最大长度。
  2. 触发UARTE的NRF_UARTE_TASK_STARTRX任务。
  3. 任务开始之后会触发NRF_UARTE_EVENT_RXSTARTED,在这个事件中断里切换缓冲区。由于UARTE的缓冲区指针是双缓冲的,所以此次设置的缓冲区为下次接收的缓冲区。
  4. 接收到数据触发NRF_UARTE_EVENT_RXDRDY事件,NRF_UARTE_EVENT_RXDRDY自动触发TIMER1的NRF_TIMER_TASK_START和NRF_TIMER_TASK_CLEAR。TIMER1的超时时间比UARTE接收一个字符的时间稍长,模拟的32类的单片机UART的IDLE功能。NRF_UARTE_EVENT_RXDRDY同时触发TIMER2的计数,记录一共接收了多少字节。
  5. NRF_UARTE_EVENT_ENDRX触发TIMER2的NRF_TIMER_TASK_CAPTURE1,获取到一共接收了多少数据,同时触发NRF_UARTE_TASK_STARTRX,将接收到的数据存入下一个缓冲区中。由于TIMER2捕获了一共接收的数据长度,则可以很容易的知道未处理的数据有多少,在中断中直接缓存数据即可。

核心代码如下:

#define AYSN_UARTE			NRF_UARTE0		// 使用uarte0 
#define PPI_CH_SETUP(_ch, _evt, _tsk, _fork)            \
    ret = nrfx_ppi_channel_assign(_ch, _evt, _tsk);     \
    if (ret != NRF_SUCCESS)                             \
    {                                                   \
        return NRF_ERROR_INTERNAL;                      \
    }                                                   \
    if (_fork)                                          \
    {                                                   \
        ret = nrfx_ppi_channel_fork_assign(_ch, _fork); \
        if (ret != NRF_SUCCESS)                         \
        {                                               \
            return NRF_ERROR_INTERNAL;                  \
        }                                               \
    }

extern gqueue_t qbtTx,qbtRx;
// 使用uarte0,超时使用timer1,计数使用timer2
static const nrfx_timer_t timeoutTimer = NRFX_TIMER_INSTANCE(1);
static const nrfx_timer_t countTimer = NRFX_TIMER_INSTANCE(2);
static nrf_ppi_channel_t uartePpiChannels[UARTE_ASYNC_PPI_CH_MAX];
static uint32_t uarteRxOneByteTimeout = 120;		// 115200波特率的时候是100us左右

ret_code_t uarte_asyn_init(uarte_asyn_config_t* pconfig)
{
	ret_code_t ret;
	uint32_t tmr_start_tsk = 0;
    uint32_t tmr_clear_tsk = 0;
    uint32_t tmr_stop_tsk = 0;
    uint32_t tmr_compare_evt = 0;
	uarteRxOneByteTimeout = pconfig->timeout_us;
	
	// 环形接收缓冲区初始化
	uarte_rx_buf_init(puarteRxBuf);
	
	// timeoutTimer init
	nrfx_timer_config_t tmr_config = NRFX_TIMER_DEFAULT_CONFIG;
	tmr_config.frequency = NRF_TIMER_FREQ_1MHz;
	tmr_config.mode = TIMER_MODE_MODE_Timer;
	tmr_config.interrupt_priority = APP_IRQ_PRIORITY_LOW;

	ret = nrfx_timer_init(&timeoutTimer, &tmr_config, tmr_evt_handler);
	if (ret != NRFX_SUCCESS)
	{
		return NRF_ERROR_INTERNAL;
	}
	nrfx_timer_compare(&timeoutTimer, NRF_TIMER_CC_CHANNEL0, uarteRxOneByteTimeout, true);

	tmr_start_tsk = nrfx_timer_task_address_get(&timeoutTimer, NRF_TIMER_TASK_START);
	tmr_clear_tsk = nrfx_timer_task_address_get(&timeoutTimer, NRF_TIMER_TASK_CLEAR);
	tmr_stop_tsk = nrfx_timer_task_address_get(&timeoutTimer, NRF_TIMER_TASK_SHUTDOWN);
	tmr_compare_evt = nrfx_timer_compare_event_address_get(&timeoutTimer, NRF_TIMER_CC_CHANNEL0);
	

	//UART init
    nrf_gpio_pin_set(pconfig->tx_pin);
    nrf_gpio_cfg_output(pconfig->tx_pin);
    nrf_gpio_cfg_input(pconfig->rx_pin, pconfig->pullup_rx ?
                        NRF_GPIO_PIN_PULLUP : NRF_GPIO_PIN_NOPULL);
    nrf_uarte_baudrate_set(AYSN_UARTE, pconfig->baudrate);
    nrf_uarte_configure(AYSN_UARTE, pconfig->parity, pconfig->hwfc);
    nrf_uarte_txrx_pins_set(AYSN_UARTE, pconfig->tx_pin, pconfig->rx_pin);
	
	IRQn_Type irqn = NRFX_IRQ_NUMBER_GET(AYSN_UARTE);
	nrf_uarte_int_enable(AYSN_UARTE, UARTE_INTERRUPTS_MASK);
    NVIC_SetPriority(irqn, pconfig->int_prio);
    NVIC_ClearPendingIRQ(irqn);
    NVIC_EnableIRQ(irqn);

    nrf_uarte_enable(AYSN_UARTE);
	
	// countTimer init
	nrfx_timer_config_t countTmr_config = NRFX_TIMER_DEFAULT_CONFIG;
    countTmr_config.mode = NRF_TIMER_MODE_COUNTER;
    countTmr_config.bit_width = NRF_TIMER_BIT_WIDTH_32;
    ret = nrfx_timer_init(&countTimer, &countTmr_config, count_tmr_evt_handler);
	
	// 分配ppi channel
	for (int i = 0; i < UARTE_ASYNC_PPI_CH_MAX; i++)
	{
		ret = nrfx_ppi_channel_alloc(&uartePpiChannels[i]);
		if (ret != NRFX_SUCCESS)
		{
			//we don't free already allocated channels, system is wrongly configured.
			return NRF_ERROR_INTERNAL;
		}
	}
	
	// RXDRDY-->TIMER1_START&TIMER1_CLEAR	
	// 		 -->TIMER2_COUNT
	// ENDRX-->RXSTART&TIMER2_C1
	// TIMER1_C0-->TIMER1_STOP&TIMER2_C0
	PPI_CH_SETUP(uartePpiChannels[UARTE_ASYNC_PPI_CH_RXRDY_CLEAR],
				 nrf_uarte_event_address_get(AYSN_UARTE, NRF_UARTE_EVENT_RXDRDY),
				 tmr_start_tsk,
				 tmr_clear_tsk);

	PPI_CH_SETUP(uartePpiChannels[UARTE_ASYNC_PPI_CH_COMPARE_SHUTDOWN],
				 tmr_compare_evt,
				 tmr_stop_tsk,
				 nrfx_timer_task_address_get(&countTimer, NRF_TIMER_TASK_CAPTURE0));
	
	PPI_CH_SETUP(
				uartePpiChannels[UARTE_DRV_PPI_CH_RXRDY_TIMER_COUNT],
				nrf_uarte_event_address_get(AYSN_UARTE, NRF_UARTE_EVENT_RXDRDY),
				nrfx_timer_task_address_get(&countTimer, NRF_TIMER_TASK_COUNT),
				0);


    PPI_CH_SETUP(
				uartePpiChannels[UARTE_DRV_PPI_CH_ENDRX_STARTRX],
				nrf_uarte_event_address_get(AYSN_UARTE, NRF_UARTE_EVENT_ENDRX),
				nrf_uarte_task_address_get(AYSN_UARTE, NRF_UARTE_TASK_STARTRX),
				nrfx_timer_task_address_get(&countTimer, NRF_TIMER_TASK_CAPTURE1));

	return NRF_SUCCESS;
}


ret_code_t uarte_asyn_enable(void)
{
	nrfx_timer_clear(&timeoutTimer);
	nrf_uarte_rx_buffer_set(AYSN_UARTE, puarteRxBuf->pwriteBuf->buf, MAX_DMA_XFER_LEN);
	puarteRxBuf->pwriteBuf = puarteRxBuf->pwriteBuf->pnext;

    /* Reset byte counting */
    nrfx_timer_enable(&countTimer);
    nrfx_timer_clear(&countTimer);

    nrf_uarte_event_clear(AYSN_UARTE, NRF_UARTE_EVENT_ENDRX);
    nrf_uarte_event_clear(AYSN_UARTE, NRF_UARTE_EVENT_RXSTARTED);

	for (int i = 0; i < UARTE_ASYNC_PPI_CH_MAX; i++)
	{
		nrfx_ppi_channel_enable(uartePpiChannels[i]);
	}
	
	// 触发接收任务
    nrf_uarte_task_trigger(AYSN_UARTE, NRF_UARTE_TASK_STARTRX);

    return NRF_SUCCESS;
}

uarte_tx_result_t uarte_asyn_send(const uint8_t* buf,uint8_t len)
{
	if(uarteTxCplt == true)
	{
		if(len > MAX_DMA_XFER_LEN)
		{
			return UARTE_TX_TOOLONG;
		}
		uarteTxCplt = false;
		nrf_uarte_tx_buffer_set(AYSN_UARTE, buf, len);
		nrf_uarte_event_clear(AYSN_UARTE, NRF_UARTE_EVENT_TXSTARTED);
		nrf_uarte_task_trigger(AYSN_UARTE, NRF_UARTE_TASK_STARTTX);
		return UARTE_TX_SUCCESS;
	}
	else
	{
		return UARTE_TX_BUSY;
	}
}


static void uarte_timeout_handler(void)
{
	NRFX_IRQ_DISABLE((IRQn_Type)NRFX_IRQ_NUMBER_GET(AYSN_UARTE));
	uint32_t rcvTotalLen = countTimer.p_reg->CC[0];
	if(rcvTotalLen > puarteRxBuf->bufStartCount + puarteRxBuf->readBufIndex)
	{
		uint32_t rcvAmount = rcvTotalLen - puarteRxBuf->bufStartCount - puarteRxBuf->readBufIndex;
		uint32_t actualPushLen = 0;
		gqueue_push_multiple(&qbtTx,&puarteRxBuf->preadBuf->buf[puarteRxBuf->readBufIndex],rcvAmount,&actualPushLen);
		puarteRxBuf->readBufIndex += rcvAmount;
	}
	NRFX_IRQ_ENABLE((IRQn_Type)NRFX_IRQ_NUMBER_GET(AYSN_UARTE));
}

// 设置了一个字符的超时时间。
// 如果dma的数据还未到达产生endrx的长度,则过了一个字符的超时时间之后,
// 可以通过此中断及时从dma的buf中获取一共收到了多少字符
static void tmr_evt_handler(nrf_timer_event_t event_type, void * p_context)
{
	uarte_timeout_handler();
}

// 没有中断,不会发生
static void count_tmr_evt_handler(nrf_timer_event_t event_type, void * p_context)
{
    UNUSED_PARAMETER(event_type);
	UNUSED_PARAMETER(p_context);
}

static void irq_handler(void)
{
    if (nrf_uarte_event_check(AYSN_UARTE, NRF_UARTE_EVENT_ERROR))
    {
        nrf_uarte_event_clear(AYSN_UARTE, NRF_UARTE_EVENT_ERROR);

    }

    if (nrf_uarte_event_check(AYSN_UARTE, NRF_UARTE_EVENT_RXSTARTED))
    {
        nrf_uarte_event_clear(AYSN_UARTE, NRF_UARTE_EVENT_RXSTARTED);
		// 切换buf
		nrf_uarte_rx_buffer_set(AYSN_UARTE, puarteRxBuf->pwriteBuf->buf, MAX_DMA_XFER_LEN);
		puarteRxBuf->pwriteBuf = puarteRxBuf->pwriteBuf->pnext;

    }

    if (nrf_uarte_event_check(AYSN_UARTE, NRF_UARTE_EVENT_ENDRX))
    {
        nrf_uarte_event_clear(AYSN_UARTE, NRF_UARTE_EVENT_ENDRX);
		// 转存数据
		uint32_t rcvTotalLen = countTimer.p_reg->CC[1];
		if(rcvTotalLen > puarteRxBuf->bufStartCount + puarteRxBuf->readBufIndex)
		{
			uint32_t rcvAmount = rcvTotalLen - puarteRxBuf->bufStartCount - puarteRxBuf->readBufIndex;
			uint32_t actualPushLen = 0;
			gqueue_push_multiple(&qbtTx,&puarteRxBuf->preadBuf->buf[puarteRxBuf->readBufIndex],rcvAmount,&actualPushLen);
		}
		// 设置新的索引
		puarteRxBuf->bufStartCount = countTimer.p_reg->CC[1];
		puarteRxBuf->readBufIndex = 0;
		// 切换下一个缓冲区
		puarteRxBuf->preadBuf = puarteRxBuf->preadBuf->pnext;
    }

    if (nrf_uarte_event_check(AYSN_UARTE, NRF_UARTE_EVENT_TXSTOPPED))
    {
        nrf_uarte_event_clear(AYSN_UARTE, NRF_UARTE_EVENT_TXSTOPPED);
		uarteTxCplt = true;

    }

    if (nrf_uarte_event_check(AYSN_UARTE, NRF_UARTE_EVENT_ENDTX))
    {
//		uint32_t actualLen = 0;
        nrf_uarte_event_clear(AYSN_UARTE, NRF_UARTE_EVENT_ENDTX);
		uarteTxCplt = true;
//		if(!gqueue_pop_multiple_most(&qbtRx,_uarteTxBuf,MAX_DMA_XFER_LEN,&actualLen))
//		{
//			zy_uarte_asyn_send(_uarteTxBuf,actualLen);
//		}

    }
}

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值