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);
//		}

    }
}

### NRF52832 UART RX Pin Low Power Configuration For configuring the UART RX pin on an NRF52832 to achieve a lower power consumption, several aspects need consideration. The configuration involves setting up the UART peripheral with specific parameters that contribute towards minimizing energy usage while ensuring reliable communication. The UART interface can be configured using Nordic's SDK or directly through registers depending upon application requirements and development environment preferences. When aiming for low-power operation of the UART receiver (RX), one should ensure that unnecessary features are disabled where possible[^1]. To configure UART RX in a way that optimizes power: - **Flow Control**: Disable hardware flow control unless absolutely necessary as it adds overhead which increases current draw. - **Baud Rate Settings**: Choose baud rates carefully since higher values increase power consumption due to more frequent sampling by the UART module. - **Interrupt Handling**: Optimize interrupt handling routines associated with receiving data over UART; this includes processing received bytes promptly without delay so that peripherals like UARTE can enter sleep mode quickly after completing transfers[^2]. - **Peripheral Mode Selection**: Utilize appropriate modes such as EasyDMA when available because these allow direct memory access reducing CPU intervention during transfer operations thus saving power[^3]. Here is how you might set up UART settings programmatically within your project codebase targeting NRF52832 devices specifically optimized for reduced power use: ```c #include "nrf_uarte.h" // ... other includes... void uart_init(void){ nrf_drv_uart_config_t config = NRFX_UART_DEFAULT_CONFIG; // Set desired Baud rate here e.g., 9600 bps config.baudrate = NRF_UART_BAUDRATE_9600; // Ensure Hardware Flow Control is OFF if not needed config.flow_control = NRF_UART_HWFC_DISABLED; // Initialize UART driver instance based on provided configurations APP_ERROR_CHECK(nrf_drv_uart_init(&config)); } ``` Additionally, consider implementing wake-up mechanisms from deep-sleep states triggered only under certain conditions related to incoming serial traffic patterns rather than keeping UART active continuously at all times[^4].
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值