打开主菜单

谷雨文档中心 β

更改

NRF52832DK基础实验

添加7,982字节2019年7月11日 (四) 11:46
UART 收发实验
=== UART 收发实验 ===
在NRF52832DK评估板上,有一路USB转串口。将nrf52832在NRF52832芯片上有一路UART外设。为此NRF52832DK评估板上,通过CH340C将UART与USB相连。这样开发者可以用PC完成UART通信收发实验。串口外设细节部分,这里不做说明。占用芯片引脚为P0.5,P0.6,P0.7,P0.8。[[文件:Uart.png|居中|缩略图|587x587像素]] ==== 代码分析 ====开发者打开谷雨物联提供的peripheral_ghostyu文件夹中08_uart_example工程(IAR工程)。 在IAR的Workspace中点开Application,双击main.c文件,打开main.c。<syntaxhighlight lang="c" line="1">//******************************************************************************// fn :main//// brief : 主程序入口//// param : none//// return : noneint main(void){ uint8_t len; LED_Init(); //LED 初始化 UART_Init(APP_UartEvtHandle); //初始化串口  bsp_board_lcd_init(); //初始化LCD使用的外设,SPI,GPIO GUI_Init(); //初始化LCD GUI_DispStringAt("Uart Test\r\n",0,0); //显示字符 //串口打印字符串 UART_Write("Uart Test\r\n",sizeof("Uart Test\r\n")-1); GUI_SetColor(GUI_BLUE); for(;;) { switch(uart_evt.evt_type) { case UART_EVT_RX_TIMEOUT: len = UART_Read(Buf,uart_evt.status); UART_Write(Buf,len); //从串口发出 Buf[len] = 0; GUI_DispString((char const*)Buf); uart_evt.evt_type = UART_EVT_NONE; break; default : break; } if(GUI_IsFull()) //判断是否为满屏 { GUI_Clear(); //清屏 GUI_GotoXY(0,0); //回到原点 FontColorChange();//改变字体颜色 } }}</syntaxhighlight>其中,UART_Init是初始化串口函数,参数是传入的事件回调函数指针,可以为NULL。<syntaxhighlight lang="c" line="1">//******************************************************************************// fn : UART_Init//// brief : 初始化串口//// param : pHandle ->处理串口事件//// return :void UART_Init(uart_user_callback pHandle){ m_uart_callback = pHandle; uart_fifo_init(); uart_timer_init(); //baudrate = 115200 nrf_drv_uart_config_t uartConfig = NRF_DRV_UART_DEFAULT_CONFIG; uartConfig.pseltxd = TX_PIN_NUMBER; uartConfig.pselrxd = RX_PIN_NUMBER; //uartConfig.pselcts = CTS_PIN_NUMBER; //uartConfig.pselrts = RTS_PIN_NUMBER;  nrf_drv_uart_init(&m_Uart,&uartConfig,uart_event_handler);  nrf_drv_uart_rx(&m_Uart, rxBuf,1);}</syntaxhighlight>在UART_Init函数中,指定串口的TX,RX引脚,不使能流控制。且格式为115200,8,N,1。在调用nrf_drv_uart_init时,传入事件回调函数指针uart_event_handler,即使用导步模式。如果传入NULL,即使用阻塞模式。在uart_event_handler事件回调函数中,监视以下三个事件# NRF_DRV_UART_EVT_RX_DONE# NRF_DRV_UART_EVT_ERROR,# NRF_DRV_UART_EVT_TX_DONENRF_DRV_UART_EVT_RX_DONE是接收完成之后产生的事件;NRF_DRV_UART_EVT_ERROR是串口发生错误时产生的事件,其中包括帧错误,无有效停止位等;NRF_DRV_UART_EVT_TX_DONE是发生完成后产生事件。 为有效控制数据帧之间的间隔,这里引入了定时器,用于监控串口接收空闲。其工作原理如下,当接收到有效数据时,打开定时器,在定时时间之内再次接收到数据时,清除定时器记数,从零重新记时。如果超时,则认为此帧数据已经结束,向上层上报接收超时事件,并携带此帧数据长度。定时器的初始化部分,不是此例程的重点,这里不做详细说明。下面将贴出定时器事件回调函数与串口事件回调函数。<syntaxhighlight lang="c" line="1">//******************************************************************************// fn : timer_uart_event_handler//// brief : 定时器事件回调函数//// param : event_type -> 事件类型// p_context -> 事件附加指针 //// return : none void timer_uart_event_handler(nrf_timer_event_t event_type, void* p_context){ switch (event_type) { case NRF_TIMER_EVENT_COMPARE0: //串口接收超时 { uart_user_evt.evt_type = UART_EVT_RX_TIMEOUT; //上报超时事件 uart_user_evt.status = UART_BUF_DATA_LEN(&m_RxBuf); //携带数据长度 if(m_uart_callback != NULL) { m_uart_callback(&uart_user_evt); } } break;  default: //Do nothing. break; }}//串口事件回调数据static void uart_event_handler(nrf_drv_uart_event_t * p_event, void* p_context){ switch (p_event->type) { case NRF_DRV_UART_EVT_RX_DONE: { //关闭超时定时器 nrf_drv_timer_disable(&m_TimerUart); //查询空闲字节 if(UART_BUF_FREE_SAPCE_LEN(&m_RxBuf)) { // fifo_set(&m_RxBuf,rxBuf); } if(UART_BUF_FREE_SAPCE_LEN(&m_RxBuf)) { nrf_drv_uart_rx(&m_Uart, rxBuf,1); //启动超时定时器 nrf_drv_timer_enable(&m_TimerUart); } else { //没有可用空间 uart_user_evt.evt_type = UART_EVT_RX_OVERFLOW; uart_user_evt.status = UART_BUF_SIZE; if(m_uart_callback != NULL) { m_uart_callback(&uart_user_evt); } } } break; case NRF_DRV_UART_EVT_ERROR: break; case NRF_DRV_UART_EVT_TX_DONE: { uint8_t tmp; if (uart_data_get(&m_TxBuf,&tmp) == NRF_SUCCESS) { nrf_drv_uart_tx(&m_Uart, &tmp, 1); } else { // Last byte from FIFO transmitted, notify the application. uart_user_evt.evt_type = UART_EVT_TX_EMPTY; if(m_uart_callback != NULL) { m_uart_callback(&uart_user_evt); } } } break; default: break; }}</syntaxhighlight>在timer_uart_event_handler函数中,主要监视定时器的超时事件,向上层上报串口接收超时事件。在uart_event_handler函数中,完成接收与发送工作。为配合串口收发工作,程序中引入了发送与接收缓存 buf_fifo_t结构。read_pos记录获取数据位置,write_pos记录写入数据位置。<syntaxhighlight lang="c" line="1">//******************************************************************************// Name : buf_fifo_t//// brief : 串口接收缓存//typedef struct{ uint8_t* pbuf; volatile uint16_t read_pos; volatile uint16_t write_pos;}buf_fifo_t;</syntaxhighlight>为方便使用,同时实现fifo_get与fifo_set两函数。fifo_get 是从read_pos位置读取数据,并更改read_pos;fifo_set 是将数据写入write_pos位置,并更新write_pos。<syntaxhighlight lang="c">static __INLINE void fifo_get(buf_fifo_t* pFifo, uint8_t * p_byte){ *p_byte = pFifo->pbuf[pFifo->read_pos]; pFifo->read_pos++; if(pFifo->read_pos >= UART_BUF_SIZE) { pFifo->read_pos = 0; }} static __INLINE void fifo_set(buf_fifo_t* pFifo, uint8_t * p_byte){ pFifo->pbuf[pFifo->write_pos] = *p_byte; pFifo->write_pos++; if(pFifo->write_pos >= UART_BUF_SIZE) { pFifo->write_pos = 0; }}</syntaxhighlight>完成串口驱动部分后,在main函数中,只要监视UART_EVT_RX_TIMEOUT与UART_EVT_RX_OVERFLOW事件,并在事件处理结构中,对串口数据进行读取并显示。 ==== 实验现象 ====硬件准备:# Jlink-Lite仿真器或J-Link仿真器# NRF52832DK评估板# TFT-LCD-144或TFT-LCD-130显示屏软件准备: 串口助手或相类似的软件 编译工程,点击IAR IDE工具栏中绿色三角仿真按钮,IAR便会将程序下载到nRF52832中,点击全速运行即可。打开串口助手,选择相应串口号,通信格式为115200,8,N,1。在串口助手中发送数据,此时NRF52832DK上的LCD显示屏会显示发送的数据,同时串口助手本身也会收到发送的数据。
119
个编辑