问题:
在使用NRF52832做串口透传的时候,示例程序里UARTE,接收长度为1个字节。如果串口接收到长数据(测试了1K),则很容易导致蓝牙发送的超时重启。
解决方案:使用PPI功能,使串口具备接收不定长数据的能力。
方案参考的是Nordic的libuarte工程,但是我们这里面有soft device,其本身会占用一些外设,比如RTC0,TIMER0等。那我们就只能选择其他外设,这里选用TIMER1和TIMER2。
PPI的配置之后的接收流程如下:
- 设置接收缓冲区,缓冲区的长度为UARTE能接受的最大长度。
- 触发UARTE的NRF_UARTE_TASK_STARTRX任务。
- 任务开始之后会触发NRF_UARTE_EVENT_RXSTARTED,在这个事件中断里切换缓冲区。由于UARTE的缓冲区指针是双缓冲的,所以此次设置的缓冲区为下次接收的缓冲区。
- 接收到数据触发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的计数,记录一共接收了多少字节。
- 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);
// }
}
}