QNX系统中基于AM3352的多串口通信驱动设计

2018-09-07 02:31
单片机与嵌入式系统应用 2018年9期
关键词:服务程序发送数据内核

(国电南京自动化股份有限公司,南京210032)

引 言

图1 多串口电路设计

串口通信具有稳定可靠、简单易实现的特点,因此在自动化领域有着广泛的应用。以一个智能变电站的通信管理服务器为例,需要设计一路调试口和多路规约通信口,同时还要支持多个串口外设,由于CPU本身提供的串口资源有限,往往不能满足上述的设计需求,而通过在FPGA上设计出支持串口协议的IP核则可以很容易地对串口进行扩展,解决CPU串口资源不足的问题。

1 原理设计

本方案用AM3352作为主控CPU,AM3352是一款基于ARM Cortext-A8内核的微处理器,主频最高支持720 MHz,运算能力高达1 600 DMIPS,具有丰富的外围接口,带6路异步串口,能搭配DDR3,支持大容量的eMMC和NAND FLASH。图1 给出了基于AM3352的多串口电路设计示意图,AM3352提供了6路本机串口UART0~UART5,另2路串口通过FPGA进行扩展。为FPGA设计了支持串口协议的IP核,对IP核进行实例化,实现了扩展串口UART7~UART8。AM3352 利用GPMC接口连接FPGA,实现对扩展串口的控制,同时将中断请求资源GPIO3_16、GPIO3_17分别分配给UART7和UART8,由FPGA通过中断的方式通知CPU读取接收缓存区存放的数据。

在串口IP核里按图2设计了寄存器组和存储器,存储器Tx_Mem和Rx_Mem用于缓存发送数据和接收数据,寄存器Tx_Len存储有效发送数据长度用于CPU通知FPGA要发送的字节数,寄存器Rx_Len存储有效接收数据长度,用于FPGA告诉CPU放在接收缓存区Rx_Mem可被读取的字节数,寄存器BUGR用于扩展串口通信速率的设置,寄存器IER用于中断使能的配置,寄存器IFR标识中断源,中断响应后应由中断服务程序对其相应位置0予以清除中断。

图2 扩展串口地址空间分配

2 QNX系统简介

QNX是类UNIX操作系统,由加拿大QSSL公司(QNX Software System Ltd.)开发的分布式实时操作系统。它采用独特的微内核结构,由内核实现进程通信、进程调度、中断处理和底层网络通信,因此内核非常小,运行速度极快。将驱动程序、应用程序、网络协议和文件系统的运行地址空间同内核的运行地址空间进行分离,使得应用程序无法直接访问内核空间,这种封闭的微内核结构保证了任何外部模块的故障都不会影响内核的运行,从而使系统的稳定性大大提高。同时,QNX支持裁剪和扩展,能针对用户需求定制不同的功能模块,实现灵活的嵌入式开发。

3 硬件配置

由于要通过GPMC模块控制FPGA来实现对扩展串口的访问控制,因此需要在驱动工作前完成对GPMC接口的配置,配置代码放在startup里,由startup设置GPMC接口的位宽、控制时钟、访问时序等,将GPMC_AD0~GPMC_AD15设置成地址数据复用的工作模式,并把起始地址0x100 0000的16 MB地址空间分配给扩展串口设备。具体配置代码如下:

out32(GPMC_CONFIG1_0, 0x2a001200);

/*NOR设备,GPMC_CLK配置成50 MHz*/

out32(GPMC_CONFIG2_0, 0x000a0a00);/*CS时序控制*/

out32(GPMC_CONFIG3_0, 0x00020201);/*ADV时序控制*/

out32(GPMC_CONFIG4_0, 0x04020602);/*OE WE时序控制*/

out32(GPMC_CONFIG5_0, 0x00040a0a);/*读写访问时序*/

out32(GPMC_CONFIG6_0, 0x030201c0);

out32(GPMC_CONFIG7_0, 0x00000F41);

/*16M地址空间,CS有效电平,基地址0x1000000*/

由于要用GPIO3_16、GPIO3_17产生中断请求信号,接下来需要对中断资源进行配置。AM3352提供了128个中断信号,而GPIO3位于第62个中断源,所以要对中断信号62进行级联,将级联后的起始中断号设置成130,从而可知GPIO3_16对应的中断号是146,GPIO3_17对应的中断号是147。此外,还需要向系统提供中断相关的callout函数,在此定义了id callout函数interrupt_id_am335x_gpio用于系统获取中断号、屏蔽和并清除中断,定义了eio callout函数interrupt_eoi_am335x_gpio用于中断服务程序结束后重新开放中断,定义了mask callout函数interrupt_mask_am335x_gpio用于屏蔽中断,定义了unmask callout函数interrupt_unmask_am335x_gpio用于开放中断。具体的中断信息结构体按下面代码定义:

const static struct startup_intrinfo intrs[] =

{

{

_NTO_INTR_CLASS_EXTERNAL, /* vector base*/

128,

/* number of vectors*/

_NTO_INTR_SPARE, /* cascade vector*/

0, /* CPU vector base*/

0, /* CPU vector stride*/

0, /* flags*/

{INTR_GENFLAG_LOAD_SYSPAGE, 0, &interrupt_id_am335x},

{INTR_GENFLAG_LOAD_SYSPAGE | INTR_GENFLAG_LOAD_INTRMASK, 0, &interrupt_eoi_am335x},

&interrupt_mask_am335x, /*maskcallout*/

&interrupt_unmask_am335x, /*unmask callout*/

0, /*config callout*/

&am335x_intc_base

},

/* GPIO3 interrupt*/

{

130, /*vector base*/

32, /* number of vectors*/

62, /* cascade vector*/

0, /*CPU vector base*/

0, /* CPU vector stride*/

0, /* flags*/

{ 0, 0, &interrupt_id_am335x_gpio},

{ INTR_GENFLAG_LOAD_INTRMASK, 0, &interrupt_eoi_am335x_gpio},

&interrupt_mask_am335x_gpio, /* mask callout*/

&interrupt_unmask_am335x_gpio, /* unmask callout*/

0,

/* config callout*/

&am335x_gpio3_base

},

};

中断请求到来后,内核首先调用第一级id callout函数, 返回第一级ID,因为有级联,内核会自动调用级联id callout函数返回级联ID,紧接着调用通过InterruptAttach绑定在级联ID上的中断服务函数interrupt handler处理用户任务,最后调用eoi callout重新开放中断。如果是通过调用InterruptAttachEvent进行中断绑定,内核会先调用mask callout屏蔽中断, 此时必须由驱动程序在中断响应函数里调用InterruptUnmask即unmask callout,重新使能中断。

4 QNX下的串口驱动

4.1 驱动工作原理

串口工作过程见图3。串口属于字符设备,由io-char进行统一管理。用户进程通过write将数据写到数据发送队列obuf,驱动调用函数tto将obuf队列里的数据发送给串口,从而实现了数据的发送。接收过程则由中断发起,CPU响应中断,最终进入串口驱动提供的中断服务程序读取串口数据,并调用函数tti将读取的数据写到数据接收队列ibuf,用户进程便可通过read读取ibuf队列中的数据。

图3 串口驱动工作原理

4.2 驱动初始化

初始化程序将串口设置成RAW工作模式,然后解析命令行参数,从命令行参数获取波特率、时钟等参数,接着创建字符串设备并注册设备名,使设备在命名空间中产生相应的名称,随后对串口进行配置、设置通信速率, 调用InterruptAttach绑定中断服务函数并开启中断,之后系统便进入消息循环处理,等待并响应中断请求。为了区分串口,在设备描述结构体里定义了dev_id成员用于记录设备ID,由设备创建函数create_device对ID赋值。驱动初始化流程见图4。

图4 初始化流程图

4.3 接收过程

接收过程由中断服务程序实现。对于扩展串口,FPGA将接收到的数据缓存到Rx_Mem,将有效数据长度写到Rx_Len,同时通过GPIO3_16或GPIO3_17向CPU发出中断请求信号,CPU响应中断请求并进入中断服务程序,在中断服务程序里调用系统函数tti将接收缓存区Rx_Mem中的有效数据放到数据接收队列ibuf,随后应用程序便可通过read读取ibuf中的数据。

对于本机串口,数据准备好后由串口控制器产生中断请求,CPU响应中断请求并进入中断服务程序,由中断服务程序调用tti函数,读取接收保持寄存器RHR里的数据并送到接收队列ibuf供应用程序读取。

4.4 发送数据及tto函数

发送数据由系统调用tto函数实现。tto函数通过调用系统函数tto_getchar从obuf队列获取待发送的数据,对于本机串口需将待发送数据写到发送保持寄存器THR启动数据发送,对于扩展串口需要将待发送数据写到发送缓存区Tx_Mem,然后将有效数据长度写到Tx_Len寄存器启动FPGA发送数据。

tto函数除了实现发送数据外,还实现了串口参数设置命令STTY、流控制命令CTRL和设备状态检测命令LINESTATUS。很多设备都带有打印机,为了避免打印机出现打印乱码,需要在驱动里实现LINESTATUS命令对打印机端口CTS进行监视,这样打印服务程序就可以通过调用devctl(fd,DCMD_CHR_LINESTATUS,&dat,4,NULL)检测打印机是否准备就绪,待打印机准备好后方能向打印口写数据。

5 脚本配置

驱动程序编写好后还需要配置脚本文件,通过脚本启动串口设备。脚本配置如下:

devc-seromap-F-S-u1-b115200-c48000000 0x44E09000^2,72 0x48022000^2,73 0x48024000^2,74 0x481A6000^2,44 0x481A8000^2,45 0x481AA000^2,46 0x1000000^2,146 0x1000300^2,147

运行脚本后便在/dev下生成了对应于串口UART0~UART7的设备文件ser1~ser8。为了方便系统调试,往往将UART0配置成console,因此需在上面脚本后添加“reopen/dev/ser1”。

结 语

许学芳、罗铭(工程师),主要研究方向为电力系统主设备继电保护。

猜你喜欢
服务程序发送数据内核
强化『高新』内核 打造农业『硅谷』
SylixOS系统的中断嵌套机制研究与实现
基于C#的进程守护程序的设计
UDP穿透NAT技术实现数据唤醒车联网T-Box设备的方案
基于嵌入式Linux内核的自恢复设计
Linux内核mmap保护机制研究
一种车载自组织网络的媒体接入控制协议
基于马尔科夫链的LoRaWAN网络节点性能分析
带标记方式的CRDSA++协议性能分析*
使用IPSec安全传输数据