教程10_nios ii软核uart的用法之二寄存器方式

上一节说了在nios ii软核中,uart的printf和scanf函数的用法。简单,强大,但是在阻塞上,存在一些小问题,用的时候要注意。

这一节说一下uart的寄存器操作方法,用过单片机的人肯定对这种方式最熟悉。接下来详细说明NIOS II软核的UART串口寄存器如何配置

一、获取数据手册

要想查看串口相关的寄存器,首先要有这个IP核的数据手册。数据手册怎么获得?

打开Qsys页面,双击打开UART模块,如下所示,点击右上角的:Documentation。

点击后,弹出:UART Documentation页面,鼠标左键点击红色框住的位置,直接跳转到浏览器。

在浏览器中,参照下面1、2、3、4顺序,依次点击:FPGA Device FamilIntel CycloneCyclone IV FPGAsEmbedded Peripherals IP User Guide

在弹出的页面中,直接点击下载即可。

二、熟悉寄存器

打开手册,11.4.3章节,可以看到UART核的各个寄存器,如图:

简单说下各个寄存器的功能。

2.1 rxdata和txdata

rxdata是数据接收寄存器,外部设备发送给串口的数据第一时间送到这里;

txdata是数据发送寄存器,NIOS II向外发出的串口数据,直接放到这里就行。

2.2 status状态寄存器

这是一个16位寄存器,状态寄存器由多个单独的位组成,这些位指示UART核心内部的特定条件。每个状态位都与控制寄存器中相应的中断使能位相关联。状态寄存器可以在任何时候被读取。读取不会改变任何位的值。向状态寄存器写入零会清除DCTS、E、TOE、ROE、BRK、FE和PE位。接下来,从低到高说一下每一位的功能。

PE,奇偶校验错误标志位。发生奇偶校验错误时,该位置一。该功能未使能的情况下,读取该位状态,一直是0。

FE,帧错误标志位。无法正确检测停止位时,该位置一。发生该错误时,需要手动清零。

BRK,Break detect,可以理解为打断检测。正常情况下,UART的RXD线应该保持高电平,如果发生意外,导致该线长时间(超过一个字节的传输时间)维持低电平,那么该位置一。需要手动清零。

ROE,接收溢出错误。串口接收缓冲器(rxdata)中的数据没拿走,新的数据又来了,就会发生这种错误。需要手动清零。

TOE,发送溢出错误。同理,txdata里面的数据还没发完,又往里写数据,就会发送这种错误。需要手动清零。

TMT,发送移位寄存器状态,非txdata。发送流程:数据写入txdata->txdata转入发送移位寄存器->发送移位寄存器逐位发送。当该寄存器空的时候,该位自动置一,发送过程中,该位值为0。

TRDY,发送就绪状态位。txdata为空时,该位为1,说明可以发送数据。向txdata写入数据后,该位变为0。

RRDY,接收成功标志位。串口成功收到数据后,该位置一;读走串口的数据后,该位清零。

E,异常标志位。前面说的TOE,ROE,BRK,FE,和PE,只要有一个置一,该位就置一。手动清零。

DCTS,CTS,EOP,用不上,暂时不考虑。

2.3 control控制寄存器

这是一个16位寄存器,它和状态寄存器类似,也是由多个单独的位组成。每个位的功能和状态寄存器里的状态基本对应,表示该功能的中断使能。接下来,从低到高说一下每一位的功能。

IPE,奇偶校验故障中断使能。

IFE,帧故障中断使能。

IBRK,打断检测中断使能。

IROE,接收溢出中断使能。

ITOE,发送溢出中断使能。

ITMT,移位寄存器空中断使能。

ITRDY,发送就绪中断使能。

IRRDY,接收成功中断使能。

IE,异常中断使能。

TBRK,发送打断信号使能。

IDCTS,RTS,IEOP,暂时不用管。

2.4 divisor寄存器

主要用于控制波特率的生成,但是UART核在生成的时候,波特率已经固定为115200,所以这个寄存器是空的,不用管。

2.5 endofpacket 寄存器

用不到,不用管。

三、创建工程

使用core_led.sopcinfo创建一个新的NIOS II工程,用于验证寄存器方式控制UART接口收发数据。创建步骤参照前面章节,工程命名为:uart_reg。

同时,由于该环境下有多个工程,需要将这两个工程相互关联。

四、UART寄存器头文件

包含UART核的NIOS II工程,在创建的时候会自动带上串口相关寄存器的头文件。打开:uart_reg_bsp下面的drivers->inc-> altera_avalon_uart_regs.h,如下:

双击打开,可以看到右侧和串口相关的寄存器相关的一些宏定义。

例如:

IOWR_ALTERA_AVALON_UART_TXDATA(base, data) ;

有基本编程经验的应该都能看懂,该函数的功能就是向寄存器:txdata写入要发送的数据。

五、编写串口相关配置函数

这部分包括串口初始化函数、接收中断相应函数、字节发送函数、字符串发送函数。接下来依次编写各个函数

5.1 串口初始化函数

因为波特率已经固定了,不需要管。这里只需要清除状态寄存器,然后开启接收中断,并给接收中断配置中断回调函数。代码如下:

void uart_init(void)
  {
  	IOWR_ALTERA_AVALON_UART_STATUS(UART_0_BASE, 0);
  	IOWR_ALTERA_AVALON_UART_CONTROL(UART_0_BASE , ALTERA_AVALON_UART_STATUS_RRDY_MSK);
  	alt_ic_isr_register(UART_0_IRQ_INTERRUPT_CONTROLLER_ID,
  						UART_0_IRQ,
  						uart0_isr_interrupt,
  	                        0,
  	                        0);
}

注册中断回调函数,这里用到的是:

int alt_ic_isr_register(alt_u32 ic_id, alt_u32 irq, alt_isr_func isr, void *isr_context, void *flags)

ic_id:中断控制ID,打开system.h文件,找到串口的中断控制ID,如下图中箭头2所示:

Irq:中断号,如上图中箭头1所示。

Isr:中断回调函数,需要自己定义,这里写的是:uart0_isr_interrupt

isr_context和flags:不用管,写0就行。

5.2 接收中断回调函数

即,发生串口接收中断时,需要执行的函数,代码如下:

void uart0_isr_interrupt(void)
  {
  	alt_u16 status_buf = 0;
  	status_buf = IORD_ALTERA_AVALON_UART_STATUS(UART_0_BASE);
  	if(status_buf & ALTERA_AVALON_UART_STATUS_RRDY_MSK)
  	{
  		data_buf = IORD_ALTERA_AVALON_UART_RXDATA(UART_0_BASE);
  		rece_flag = 1;
  	}
  	IOWR_ALTERA_AVALON_UART_STATUS(UART_0_BASE, 0);
}

在这个函数里,先读状态寄存器,看发生中断的是不是串口接收那里。是的话读取接收缓冲器里面的值,并置一一个标志位。

5.3 字节发送函数

即,实现单个字节的发送,代码如下:

void send_byte(alt_8 c_send)
{
	while(!(IORD_ALTERA_AVALON_UART_STATUS(UART_0_BASE) & ALTERA_AVALON_UART_STATUS_TRDY_MSK));
	IOWR_ALTERA_AVALON_UART_TXDATA(UART_0_BASE, c_send);
}

在这个函数中,先判断发送就绪标志位是否置一。是的话,说明可以发送,把数据送进去;不是的话,等待,等到就绪。

5.4 字符串发送函数

在单个字节发送的基础上,实现字符串的发送,代码如下:

void send_str(alt_u8 * addr)
  {
  	while(*addr != '\0')
  	{
  		send_byte(*addr++);
  	}
}

常用单片机的都懂,不解释。

5.5 主函数部分

主函数这里,做了个简单的判断,当收到的字符是’a’时,LED对应的端口输出高电平;当收到的字符是’b’时,LED对应的端口输出低电平;当收到的字符是’c’时,输出字符串:“Hello uart!”;当收到的字符是其它值时,输出字符串:“Try again!”。

六、编译、烧录、测试

代码编写完成后,保存、编译。没有问题的话,进行烧录。

测试效果如上,程序按照我们需要的功能执行。 打完收工!

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据