基于DSP嵌入式uClinux的串口驱动程序开发
2018-09-20董纯蒋方亮季启政杨林鹏
董纯, 蒋方亮, 季启政, 杨林鹏
(北京东方计量测试研究所,北京 100086)
0 引 言
在多串口应用中,使用多片ST16C2552为BF518扩展串口设备。在uClinux系统中,基于字符设备驱动模型[1]开发ST16C2552串口驱动程序存在以下不足:第一,字符设备驱动模型只提供最基本的和操作系统的接口,不实现任何通用逻辑,因此所有串口功能都需要编程实现,比如数据缓冲逻辑、阻塞逻辑和配置命令等,开发和测试成本高;第二,为每个串口设备单独开发驱动,由于同型号芯片操作方式相同,会产生大量重复代码,代码复用性差且不支持扩展。采用uClinux平台设备驱动技术和TTY终端设备驱动模型进行串口驱动程序开发,TTY终端设备驱动中已经实现了串口通用逻辑,因此只需对标准接口进行实现[2],就可以开发出功能完备的串口驱动程序,缩短了开发和测试周期。当硬件连接更多扩展芯片时,在系统中创建对应的平台设备,就可以实现驱动程序的复用和扩展。
1 串口扩展硬件原理
图1描述了使用ST16C2552扩展串口设备的硬件连接方式。为了提高BF518异步总线[3]的扩展能力,使用CPLD对BF518异步总线的高位地址线(A16-A19)进行译码,产生多个片选信号。BF518通过CPLD连接了4片ST16C2552芯片,一片ST16C2552提供2个独立串口,因此总共扩展出8个串口。为每个串口分配一个用来连接中断信号线的GPIO。
图1 BF518与ST16C2552硬件连接图
2 uClinux下的串口驱动程序
2.1 平台设备驱动
平台设备驱动架构包含总线、设备和驱动,总线自动完成设备和驱动的匹配工作[4]。使用平台设备驱动技术可以隔离板级信息和驱动程序,在平台设备信息中定义设备使用的资源、设备的具体配置信息,而在驱动中,使用标准API去获取资源和信息,做到了板相关代码和驱动代码的分离,使驱动具有扩展性。
2.2 平台设备驱动的注册与卸载
ST16C2552驱动程序使用module_init(st16c2552_uart_init)与module_exit(st16c2552_uart_exit)指定加载函数与卸载函数。st16c2552_uart_init函数调用uart_register_driver和platform_driver_register完成uart_driver和platform_driver的注册,st16c2552_uart_exit函数调用uart_unregister_driver和platform_driver_unregister完成uart_driver和platform_driver的卸载。
2.3 平台设备接口和驱动接口的实现
platform_device是平台设备接口,对于每个串口设备,都需要实现该接口。platform_device数据结构包含name、id、resource等成员变量。name是平台驱动中实现驱动和设备匹配的关键,总线会根据name寻找对应的设备驱动[5]。id字段用来区分name字段完全相同的平台设备。resource是资源数组,将内存基地址和中断号信息抽取出来保存到资源数组中,是实现设备相关代码与驱动代码分离的关键。根据ST16C2552的芯片特性,在这个数组中定义了两个元素:基地址资源、中断号资源。数组元素中的flags字段表示该资源类别,IORESOURCE_MEM表示内存资源,IORESOURCE_IRQ表示中断资源。驱动程序需要支持8个串口,因此建立8个platform_device,每个platform_device的name成员设置相同内容,id成员从0依次累加,根据实际情况设置每片ST16C2552的基地址和中断号。
platform_driver是平台驱动接口,需要为probe和remove函数指针及name变量赋值,platform_device和platform_driver匹配时会调用probe指向的函数,注销platform_device或platform_driver时会调用remove指向的函数。name字段用来匹配对应的platform_device。
platform_driver中的probe函数主要完成驱动程序的初始化工作,首先创建uart_port,然后使用标准API获取platform_device中的基地址和中断号信息,对uart_port进行初始化,最后通过调用dev_set_drvdata函数设置uart_port与platform_device关联,调用uart_add_one_port函数设置uart_port与uart_driver关联。上述操作完成后,系统中会生成与uart_port对应的设备节点,设备名称由uart_driver的dev_name与uart_port的line编号组成。
2.4 TTY终端设备驱动接口的实现
1)uart_driver与uart_port接口的实现
在驱动程序中实现uart_driver接口,该接口driver_name字段是驱动名称,dev_name字段是设备名称,major是主设备号,minor是从设备起始号,nr是uart_driver对应串口设备的最大数量,硬件上扩展了8个串口,所以设定为8。
uart_port用来保存了串口设备信息,初始化uart_port时,需要设置晶振频率、发送FIFO大小、line编号、iotype 等参数字段,还要为其关联uart_ops操作函数集。uart_ops包含与硬件相关的底层操作函数接口,需要编程实现这些操作函数。
2)startup函数的实现
在应用层调用open函数时,会最终调用uart_ops中的startup函数,该函数主要完成申请中断、设置中断处理函数以及使能ST16C2552接收中断的操作。为了实现驱动程序的复用性,ST16C2552芯片寄存器的访问地址通过计算方式获得,使用基地址加上ST16C2552芯片内部寄存器的偏移地址,得到硬件访问地址。
3)set_termios函数的实现
在应用层进行串口参数设置时,会最终调用uart_ops中的set_termios函数,实现设置串口参数,包括数据位、停止位、奇偶校验和波特率等功能。该函数获取来自应用层的termios数据结构,从该数据结构中读取串口参数的设置值,根据波特率计算分频值,然后关闭ST16C2552中断使能,将分频值写入DLL和DLM寄存器,将设置值写入LCR寄存器,最后使能ST16C2552中断,根据最新的设置值更新端口的timeout值。
4)start_tx函数的实现
用户发送数据时,通过“write()系统调用-TTY核心-线路规程”的层层调用,最终调用uart_ops中的start_tx函数。在start_tx函数中,首先在ST16C2552中断使能寄存器中设置发送中断使能位,然后将发送缓冲区数据依次传递给ST16C2552发送寄存器。发送缓冲区中数据比较多时,未发送的数据会在后续的发送中断处理程序中发送出去。
5)中断服务程序的实现
ST16C2552提供了基于优先级的中断管理机制,ISR(中断状态寄存器)中总是保存当前优先级最高的中断代码,其余中断则保存到队列中等待后续服务,需要将中断触发方式设置为电平触发,在中断处理程序中读取ISR寄存器,根据中断代码判断中断类型,然后进行相应处理。
图2是中断处理函数流程图。首先读取ST16C2552中断状态寄存器获得中断代码,然后判断中断类型。如果是接收中断,先读取线路状态寄存器,然后读取数据接收寄存器,接收数据计数累加,判断是否产生错误,有则设置错误标志,调用uart_insert_char函数,将接收到的数据拷贝到tty_struct结构体的buf缓冲区中,最后调用tty_flip_buffer_push将数据拷贝到tty_struct结构体的read_buf缓冲区中。如果是发送中断,意味着硬件已经将之前的数据发送完毕,串口发送硬件处于空闲状态,这时可以将新的数据传递给硬件。程序先检查发送缓冲区中数据的数量和停止发送标志,如果发送数据缓冲区里没有待发送的数据或停止标志有效,就将ST16C2552中断使能寄存器的发送中断使能标志清零,禁用发送中断,以减少对CPU资源的消耗,接下来检查是否设置了高优先级的字符,如果设置了,就先发送高优先级字符,然后将发送数据缓冲区里的数据依次写入ST16C2552的发送寄存器,由硬件发送,每次将数据写入发送寄存器时,对已发送字符个数进行一次累加,最后检查发送缓冲区中剩余字符的个数,如果小于256个,就唤醒向发送缓冲区写数据的线程。
3 串口驱动程序测试
3.1 驱动安装
使用insmod指令将所有platform_device和platform_driver安装到系统中,进入/dev目录,执行ls命令列出所有设备节点,系统自动生成tty_ST16C2552_UART0~tty_ST16C2552_UART7共8个串口设备节点。
3.2 驱动调用测试
首先使用open系统调用打开串口,然后使用tcgetattr函数获取当前的termios数据结构,设置8数据位、1停止位、无奇偶校验、波特率9 600 bps、原始输入模式,设置更新termios数据结构[6],循环中使用read系统调用接收数据,并将接收到的数据通过write系统调用发送出去。将测试板串口与计算机串口相连,在计算机上运行串口调试助手,设置与测试程序一致的串口参数,使用串口调试助手发送数据,停止发送后观测到发送与接收字节数及数据内容都一致,证明串口驱动工作正常。
图2 中断处理函数流程图
4 结束语
使用平台设备驱动技术及TTY终端设备驱动模型相结合的方法开发了串口驱动程序,并编写应用程序对驱动程序进行调用测试,测试结果显示该驱动程序实现了预期功能,目前已经投入到项目使用中。使用该方案开发串口驱动程序主要有两个优点:第一,当硬件连接更多ST16C2552时,只需编写platform_device并动态加载到系统中,可实现对新增ST16C2552串口设备的支持,体现了驱动程序的动态扩展特性;第二,uClinux下的TTY设备驱动模型已包含通用逻辑的实现,只需要实现它提供的一组接口函数,就可以开发出功能完备的串口驱动程序,降低了开发和测试成本,提高了驱动程序的可靠性。