电子说
周立功教授新书《面向AMetal框架与接口的编程(上)》,对AMetal框架进行了详细介绍,通过阅读这本书,你可以学到高度复用的软件设计原则和面向接口编程的开发思想,聚焦自己的“核心域”,改变自己的编程思维,实现企业和个人的共同进步。经周立功教授授权,即日起,致远电子公众号将对该书内容进行连载,愿共勉之。
第五章为深入浅出AMetal,本文内容为5.8 UART 总线。
5.8 UART 总线
UART(Universal Asynchronous Receiver/Transmitter)是一种通用异步收发传输器,其使用串行的方式在双机之间进行数据交换,实现全双工通信,数据引脚仅包含用于接收数据的RXD 和用于发送数据的TXD。
>>> 5.8.1 初始化
在使用UART 接口前,必须先完成UART 的初始化,获取标准UART 实例句柄。在LPC82x 中,能够提供UART 服务的外设有USART0、USART1 和USART2。注意,USART和UART 的概念很容易混淆,USART 比UART 多了一个S——同步(Synchronous),USART支持同步和异步两种通信方式,而UART 仅支持异步通信方式。同步方式与异步方式主要的区别在于,同步方式需要使用一根时钟线进行时钟的同步。由于使用较多的是异步通信方式,因此AMetal 仅仅提供了异步通信方式的通用接口,即LPC824 的USART 外设也仅被用作UART。为了方便用户使用,AMetal 提供了与外设对应的实例初始化函数,详见表5.23。
表5.23 UART 实例初始化函数(am_lpc82x_inst_init.h)
这些函数的返回值均为am_uart_handle_t 类型的UART 实例句柄,该句柄将作为UART通用接口中handle 参数的实参。类型am_uart_handle_t(am_uart.h)定义如下:
由于函数返回的UART 实例句柄仅作为参数传递给UART 通用接口,因此不需要对该句柄作其它任何操作。若函数返回的实例句柄的值为NULL,则表明初始化失败,不能使用该实例句柄。如果使用串口0,则调用串口0 实例初始化函数,即可获取对应的实例句柄:
>>> 5.8.2 接口函数
AMetal 提供了5 个与UART 相关的标准接口函数(am_uart.h),详见表5.24。
表5.24 UART 标准接口函数
1. UART 控制
实现UART 的常见的控制,比如,波特率、数据位数和奇偶校验等。其函数原型为:
常见命令与对应的p_arg 参数,UART 硬件参数与UART 模式分别详见表5.25、表5.26和表5.27。
表5.25 UART 常用控制命令
表5.26 UART 硬件参数
表5.27 UART 模式
为了便于传递不同类型的值,通常将p_arg 设置为void *类型。比如,在设置波特率时,其类型为uint32_t;而在获取波特率时,其类型为 uint32_t *。如果返回AM_OK,说明本次控制命令执行成功;如果返回-AM_EINVAL,说明因参数无效导致控制命令执行失败。比如,设置UART 为查询模式,波特率为115200,8 位数据位,无校验,1 位停止位,使用范例详见程序清单5.101。
程序清单5.101 am_uart_ioctl()范例程序
由于对应控制命令的参数均为uint32_t 类型,而函数形参为void *,因此需要使用强制类型转换将uint32_t 强制转换为 void *。
2. UART 发送(查询模式)
如果以查询的方式发送UART 数据,则该函数会等待发送结束后返回。其函数原型为:
在使用该函数前,应确保UART 工作在查询模式,其返回值为成功发送数据的个数。比如,发送一个字符串"Hello World! ",详见程序清单5.102。
程序清单5.102 am_uart_poll_send()范例程序
3. UART 接收(查询模式)
如果以查询的方式接收UART 数据,则该函数会等到指定个数的数据接收完成后返回。其函数原型为:
在使用该函数前,应确保UART 工作在查询模式,其返回值为成功接收数据的个数。比如,从串口0 接收10 个数据,详见程序清单5.103。
程序清单5.103 am_uart_poll_receive()范例程序
4. 设置回调函数(中断模式)
由于在查询模式下收发数据会阻塞程序,因此最好的方式是使用中断模式发送数据。当中断事件发生时,通过回调函数与应用程序交互。其函数原型为:
其中,callback_type 表示本次设置的类型,即设置发送回调函数还是接收回调函数,详见表5.28。
表5.28 callback_type 的含义(am_uart.h)
以获取一个待发送的字符为例,pfn_callback 参数的类型为am_uart_txchar_get_t:
当可以发送一个字符时,UART 驱动将自动调用设置的该类型回调函数,p_arg 为用户自定义的参数(即为设置该类型回调函数时指定的p_arg),p_char 指针用于获取一个待发送的字符,详见程序清单5.104。
程序清单5.104 设置“获取发送数据的回调函数”范例程序
以提交一个已经接收到的字符为例,pfn_callback 参数的类型为am_uart_rxchar_put_t:
当接收一个字符时,UART 驱动将自动调用设置的该类型函数,p_arg 为用户自定义的参数(即为设置该类型回调函数时指定的p_arg),ch 为接收的字符数据,详见程序清单5.105。
程序清单5.105 设置“提交已接收数据回调函数”范例程序
5. 启动发送(中断模式)
如果UART 使用中断模式发送数据,当UART 发送器为空时,就会调用设置的“获取发送数据的回调函数”以获取发送数据。其函数原型为:
使用中断模式收发数据的范例程序详见程序清单5.106。
程序清单5.106 中断模式范例程序
该程序的功能是将每次收到的10 个数据原封不动地发送出去,am_main()函数中的主循环可以不参与任何处理,即可实现串口数据的收发。
>>> 5.8.3 带缓冲区的UART 接口
由于查询模式会阻塞整个应用,因此在实际应用中几乎都使用中断模式。在中断模式下,UART 每收到一个数据都会调用回调函数。如果将数据的处理放在回调函数中,很有可能因当前数据的处理还未结束而丢失下一个数据。基于此,AMetal 提供了带缓冲区的UART 接收函数,其实现是在UART 中断接收与应用程序之间,增加一个接收缓冲区。当串口收到数据时,将数据存放在缓冲区中,应用程序直接访问缓冲区即可。
对于UART 发送,虽然不存在丢失数据的问题,但为了便于开发应用程序,避免在UART中断模式下的回调函数接口中一次发送单个数据,同样提供了带缓冲区的UART 发送函数。当应用程序发送数据时,将发送数据存放在发送缓冲区中,串口在发送空闲时提取发送缓冲区中的数据进行发送。基于此,AMetal 提供了一组带缓冲区的UART 通用接口,详见表5.29。如无特殊需求,均建议使用带缓冲区的UART 通用接口。
表5.29 带缓冲区的UART 通用接口函数(am_uart_rngbuf.h)
1. 初始化
指定关联的串口外设(相应串口的实例句柄handle),以及用于发送和接收的数据缓冲区,初始化一个带缓冲区的串口实例,其函数原型为:
其中, p_dev 为指向am_uart_rngbuf_dev_t 类型的带缓冲区的串口实例指针,在使用时,只需要定义一个am_uart_rngbuf_dev_t 类型(am_uart_rngbuf.h)的实例即可:
其中,g_uart0_rngbuf_dev 为用户自定义的实例,其地址作为p_dev 的实参传递。handle为 UART 实例句柄,用于指定该带缓冲区的串口实际关联的串口。p_rxbuf 和rxbuf_size 用于指定接收缓冲区及其大小,p_txbuf 和txbuf_size 用于指定发送缓冲区及其大小。
函数的返回值为带缓冲区串口的实例句柄,可用作其它通用接口函数中handle 参数的实参。其类型am_uart_rngbuf_handle_t(am_uart_rngbuf.h)定义如下:
如果返回值为NULL,表明初始化失败,初始化函数使用范例详见程序清单5.107。
程序清单5.107 am_uart_rngbuf_init()范例程序
虽然程序将缓冲区的大小设置为128,但实际上缓冲区的大小应根据实际情况确定。若接收数据的缓冲区过小,则可能在接收缓冲区满后又接收新的数据发生溢出而丢失数据。若发送缓冲区过小,则在发送数据时很可能因为发送缓冲区已满需要等待,直至发送缓冲区有空闲空间而造成等待过程。
2. 发送数据
发送数据就是将数据存放到am_uart_rngbuf_init()指定的发送缓冲区中,串口可以进行数据发送时(发送空闲),从发送缓冲区中提取需要发送的数据进行发送。其函数原型为:
该函数将数据成功存放到发送缓冲区后返回,返回值为成功写入的数据个数。比如,发送一个字符串“Hello World!“,详见程序清单5.108。
程序清单5.108 am_uart_rngbuf_send()范例程序
注意,当该函数返回时,数据仅仅只是存放到了发送缓冲区中,并不代表已经成功地将数据发送出去了。
3. 接收数据
接收数据就是从am_uart_rngbuf_init()指定的接收缓冲区中提取接收到的数据,其函数原型为:
该函数返回值为成功读取数据的个数,使用范例详见程序清单5.109。
程序清单5.109 am_uart_rngbuf_receive()范例程序
4. 控制函数
与UART 控制函数类似,用于完成一些基本的控制操作。其函数原型为:
“控制命令”和“对应命令的参数”,与UART 控制函数am_uart_ioctl()的含义类似。带缓冲区的UART 可以看作是在UART 基础上的一个扩展,因此绝大部分UART 控制函数的命令均可直接使用。之所以不支持“模式设置”命令,是因为带缓冲区的UART 在初始化后始终工作在中断模式,不能修改为查询模式。除支持串口控制函数的绝大部分命令外,还新定义了一些扩展命令,详见表5.30。
表5.30 带缓冲区的UART 扩展控制命令
前5 条命令都是用于操作缓冲区的一些命令,“获取可读数据的个数”命令用于获取接收缓冲区中已经接收的数据个数,“获取已经写入数据的个数”命令可以获取当前已经写入发送缓冲区中的数据个数。当不再需要发送或接收缓冲区的数据时,即可直接使用“清空缓冲区”命令将对应的缓冲区直接清空,其相应的范例程序详见程序清单5.110。
程序清单5.110 am_uart_rngbuf_ioctl()范例程序
“设置读超时时间”命令用于设置读超时时间,该时间在am_uart_rngbuf_receive()接收数据时起作用。在接收数据时会指定接收数据的个数,在接收数据过程中,可能由于接收缓冲区中无数据可读而进入等待状态。在默认情况下,如果没有设置读超时时间,则会一直等到数据接收完成(接收到指定的数据个数)才会返回。当设置了超时时间后,假设设置超时时间为50ms,则在等待过程中超过50ms 都没有接收到任何新数据时,函数同样会返回,其返回值为实际接收到的数据个数,其相应的范例详见程序清单5.111。
程序清单5.111 设置超时时间范例程序
注意,超时时间并不是每次接收数据前都需要设置,往往只需要设置一次。如果需要修改超时时间,可以使用该函数重新设置一个超时时间。特别地,若希望接收数据不足时立即返回,则可以设置超时时间为AM_NO_WAIT(am_common.h),若需要恢复为一直等待到数据接收完成才返回,则可以设置超时时间为AM_WAIT_FOREVER(am_common.h)。
全部0条评论
快来发表一下你的评论吧 !