NRF52芯片模拟物理串口

UART模拟串口

  • 什么是UART
  • 模拟串口方法
    • 纯软件模拟
    • 硬件+软件的模拟方法
  • 功耗情况
    • 接收功耗
    • 发送功耗
  • 波特率支持情况
    • 最大波特率
    • 最小波特率
  • 驱动程序
    • 初始化
    • 关闭串口
    • 发送数据
    • 停止接收
    • ADC数据位解析
  • 典型应用代码示例
  • 参考网站

什么是UART

UART — Universal asynchronous receiver/
transmitter,通用异步数据接收/发送器。
UART典型使用三根线,TX,RX,GND,此处不涉及到RTS,CTS。NRF52芯片只有一个物理串口,如果需要NRF52芯片同时连接两个以上的串口外设就需要模拟更多的串口出来。

模拟串口方法

纯软件模拟

通常可以采用GPIO中断和定时判断高低电平这类纯软件的方式去模拟接收,使用延时控制IO口高低电平的方式去模拟发送,这种方法的缺点很明显,那就是在BLE功能开启的情况下,GPIO和定时器中断都会被延迟,从而导致无法正确接收和发送数据位。即使在无BLE开启的情况下,由于CPU处理中断的延时也会导致正常传输数据的波特率不会太高。

硬件+软件的模拟方法

本文着重讲一种纯硬件方式去模拟串口的方法,模拟发送数据使用NRF52的PWM+DMA的方式,高位1的点空比为0,低为0的点空比为每位的总时长;模拟接收数据采用NRF52的GPIOTE+PPI+SAADC+DMA的方式,当接收到数据流的起始位时GPIOTE中断会触发ADC开始按每位时长进行采样,采样值超过门限值为1,反之为0,DMA将ADC的每位采样值存储到指定内存空间,当采集完成后,CPU参与解析接收到的每位采样值。
接收数据有三个比较麻烦的地方,一是需要额外增加一个IO口扮演RX的角色,这个IO主要用作判断数据流的起始位,在数据接收过程中,禁止此IO中断,数据接收完成后才重新打开中断监听新的数据流;二是ADC停止采样后,不能在ADC中断中立即设置新的接收BUFFER,需要等ADC完全停止后才能重新设置,否则ADC无法停止采样;三是采样到的数据解析,由于串口每位的时间无法和时钟的TICK完全整除,所以我们采样的时间点一定会有误差,我们做不到精确的在每一位进行一次采样,可以通过变通的方法,每次采样的时间是每位时长的一半多2-5个TICK,那么可以保证每位最多两次采样且至少会有一次采样,解析时如果发现缺少一个相同的采样位,并且与下一个采样位值不同,我们认为此次采样为一个有效的位。

功耗情况

接收功耗

接收时开启GPIOTE+SAADC,GPIOTE功耗1微安,SAADC每次采样700微安约5微秒,不采样时5微安。可以根据不同波特率每位2次采样计算出SAADC功耗。(暂时未能实测功耗)

发送功耗

发送时开启PWM和IO输出,PWM的基础时钟频率设置为8 MHz时电流为150微安,IO输出高电平电流约2毫安,视发送的波特率和高低位情况而定。(IO输出口是否可以增加大电阻降低电流,请硬件确认)

波特率支持情况

最大波特率

//理论上可以达到100KBPS
UART_V_BAUDRATE_BAUDRATE_57600

最小波特率

UART_V_BAUDRATE_BAUDRATE_4800

驱动程序

初始化

		//接收初始化GPIOTE+SAADC
		if(pin_dt != UART_VIR_PIN_INVALID_NUMBER)
		{
			nrf_drv_gpiote_init();
			nrf_drv_gpiote_in_config_t cfg = NRFX_GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
			nrfx_err_t err_code = nrf_drv_gpiote_in_init(pin_dt, &cfg, NULL);
			APP_ERROR_CHECK(err_code);
			nrf_drv_gpiote_in_event_enable(pin_dt, true);
		}

#if(UART_VIR_SWI_ENABLE)
// Enable TRIGGERED0 interrupt
NRF_EGU3->INTENSET = EGU_INTENSET_TRIGGERED0_Enabled << EGU_INTENSET_TRIGGERED0_Pos;
// Set EGU3 Interrupt pririty to 6.
NVIC_SetPriority(SWI3_EGU3_IRQn, UART_VIR_IRQ_PRIORITY);
//Enable SWI3_EGU3 interrupt.
NVIC_EnableIRQ(SWI3_EGU3_IRQn);
#endif
adc_sampler_init(band_rate);
adc_sampler_pin_init(pin_rx);
APP_ERROR_CHECK(nrfx_saadc_buffer_convert(uart_v_ctx.adc_vals[0], UART_VIR_SAMPLE_BUFFER_SIZE));
APP_ERROR_CHECK(nrfx_saadc_buffer_convert(uart_v_ctx.adc_vals[1], UART_VIR_SAMPLE_BUFFER_SIZE));
nrfx_saadc_calibrate_offset();

		// 发送初始化PWM
		nrf_gpio_cfg_output(pin_tx);
		nrf_gpio_pin_set(pin_tx);
		pwm_transmitter_init(band_rate, pin_tx);

关闭串口

		adc_sampler_pin_uninit();
		adc_sampler_uninit();
		pwm_transmitter_uninit(uart_vir_ctx.pin_tx);

发送数据

	uint32_t err_code;
	int16_t *ptr = uart_v_ctx.pwm_vals;
	CRITICAL_REGION_ENTER()
	len = (len <= (UART_V_SAMPLE_BUFFER_SIZE / PWM_BITS_PER_BYTE) ? len : UART_V_SAMPLE_BUFFER_SIZE / PWM_BITS_PER_BYTE);
	for(uint16_t i = 0; i < len; i++)
	{
			PWM_BUFFER_DATA_BYTE(ptr, data[i], uart_v_ctx.pwm_dict);
			ptr += PWM_BITS_PER_BYTE;
	}
	for(uint16_t i = 0; i < UART_BITS_END; i++)
	{
			*ptr++ = uart_v_ctx.pwm_dict[PWM_VAL_END][0];
	}
	rtn = len;
	uart_v_ctx.tx_bits_count = len * PWM_BITS_PER_BYTE + UART_BITS_END;
	if (pwm_transmitter_tx_start() == NRF_ERROR_BUSY)
	{
			rtn = 0;
	}
	CRITICAL_REGION_EXIT()

停止接收

		nrfx_saadc_abort();
		APP_ERROR_CHECK(nrfx_saadc_buffer_convert(uart_v_ctx.adc_vals[0], UART_V_SAMPLE_BUFFER_SIZE));
		APP_ERROR_CHECK(nrfx_saadc_buffer_convert(uart_v_ctx.adc_vals[1], UART_V_SAMPLE_BUFFER_SIZE)); 

ADC数据位解析

	//将接收到的第一个采样值补充到前次解析中,判断是否是上一块数据最后一位的第二个采样值,如果是i为1,否则i为0
	for(; i < count; i += ADC_SAMPLE_PER_BIT)
	{
			bitval = ADC_VAL_BIT_P(ptr[i], bitval);
			val = val | (bitval << bits);
			bits += 1;
			if(i + 1 < count)
			{
				if(!ADC_VAL_CHECK_BIT(ptr[i + 1], 1, bitval))
				{
					i -= 1;
				}
			}
			else 
			{
				continue;
			}
			if(bits == UART_VIR_BUF_BYTE_BITS)
			{
				bits = 0;
				if(UART_10BIT_CHECK(val))
				{
						uart_vir_ctx.rx_bytes[idx++] = (val >> 1);
				}
				else if(UART_STOP_CHECK(val))
				{
						break;
				}
				else
				{
					bits = i;
					bits = 0;
				}
				val = 0;
			}
	}
	//解析完成后,保留没有解析完整的BYTE和相关位的情况,等待下一块数据接着解析
	//解析中如果发现数据块的最后一部分有连续超过20个采样位都是高电平,则认为接收结束,应该关闭SAADC
		if(UART_STOP_CHECK(val))
		{
			nrfx_saadc_abort();
			uart_v_ctx.rx_state = UART_V_TRX_STATE_STOP;

#if(UART_VIR_SWI_ENABLE)
NRF_EGU3->TASKS_TRIGGER[0] = 1;
#endif
}

典型应用代码示例

//打开串口
uart_v_open(PIN_PC_TXD, PIN_PC_RXD, PIN_PC_DEC, UART_V_BAUDRATE_BAUDRATE_57600, uart_v_data_handler);

//发送数据
uart_v_send("uart_v rx = 15 tx = 16, running...\r\n", 37);

//接收数据回调函数

void uart_v_data_handler(uart_v_evt_type_t evt, uint8_t *ptr, uint8_t len)
{
NRF_LOG_INFO(“uart_v_data_handler evt = %d, len = %d, ticks = %d”, evt, len);
switch(evt)
{
case UART_V_EVT_TX_DONE:
break;
case UART_V_EVT_RX_DONE:
break;
case UART_V_EVT_ERROR:
break;
default:
break;
}
}

参考网站

1: http://www.nordicsemi.com/
2: https://infocenter.nordicsemi.com/index.jsp
3: https://devzone.nordicsemi.com/

你可能感兴趣的:(技术)