目录
目的功能:当串口接收数据时,触发中断,然后将接收到的数据再发送出去。
1. 了解UART IP核
下图是FPGA实现NIOS II 软核的整个框架,在搭建NIOS II时,内部可能会调用很多IP核来完成数据,代码的存储、JTAG功能以及串口通信等。NIOS II核与内部其他的IP核之间的通信是通过Avalon 总线的内部互连线连接。
从下面的图可以看出,UART核内部有6个寄存器,分别是divisor、 rxdata、 state、 txdata、 control、 endofpacket,endofpacket可以在设置中配置是否使用, rxdata和txdata保存从移位寄存器中接收的数据和需要发给移位寄存器的数据,然后从state状态寄存器可以查看uart在收发数据过程中的状态,control就是对应d额不同寄存器状态可以选择是否产生中断。
2. 如何用串口发送数据,接收数据?中断如何使用?
为了方便控制uart,可以通过构建结构体,将uart操作过程中需要的函数、状态标志、缓存区以及中断等全部封装成结构体的成员。在使用时就可以直接对结构体操作。
方式1
1)新建inc文件夹,写sopc.h和uart.h
SOPC.h中主要是UART对应用结构体的编写,用于直接对寄存器进行控制的代码。主要包含5个寄存器,存储接收到的数据的RXDATA寄存器(从I/O接收的数据会先存放到此处)、存储需要发送的数据的TXDATA寄存器(需要发送出去的数据,会先放到这个寄存器然后发送)、控制中断等的CONTROL寄存器、表示串口状态的STATUS寄存器,还有存储波特率数据的DIVISOR寄存器。
#ifndef SOPC_H
#define SOPC_H
#include "system.h"
#define _UART
typedef struct
{
union
{
struct
{
volatile unsigned long int RECEIVE_DATA :8;
volatile unsigned long int NC :24;
}BITS;
volatile unsigned long int WORD;
}RXDATA;
union
{
struct
{
volatile unsigned long int TRANSMIT_DATA :8;
volatile unsigned long int NC: 24;
}BITS;
volatile unsigned long int WORD;
}TXDATA;
union
{
struct
{
volatile unsigned long int PE :1;
volatile unsigned long int FE :1;
volatile unsigned long int BRK :1;
volatile unsigned long int ROE :1;
volatile unsigned long int TOE :1;
volatile unsigned long int TMT :1;
volatile unsigned long int TRDY:1;
volatile unsigned long int RRDY:1;
volatile unsigned long int E :1;
volatile unsigned long int NC :1;
volatile unsigned long int DCTS:1;
volatile unsigned long int CTS :1;
volatile unsigned long int EOP :1;
volatile unsigned long int NC1 :19;
}BITS;
volatile unsigned long int WORD;
}STATUS;
union
{
struct
{
volatile unsigned long int IPE :1;
volatile unsigned long int IFE :1;
volatile unsigned long int IBRK :1;
volatile unsigned long int IROE :1;
volatile unsigned long int ITOE :1;
volatile unsigned long int ITMT :1;
volatile unsigned long int ITRDY:1;
volatile unsigned long int IRRDY:1;
volatile unsigned long int IE :1;
volatile unsigned long int TRBK :1;
volatile unsigned long int IDCTS:1;
volatile unsigned long int RTS :1;
volatile unsigned long int IEOP :1;
volatile unsigned long int NC :19;
}BITS;
volatile unsigned long int WORD;
}CONTROL;
union
{
struct
{
volatile unsigned long int BAUD_RATE_DIVISOR:16;
volatile unsigned long int NC :16;
}BITS;
volatile unsigned long int WORD;
}DIVISOR;
}UART_ST;
#ifdef _UART
#define UART ((UART_ST*)(UART_BASE|(1<<31)))
#endif //_UART
#endif //SOPC_H
因为在NIOS II/F内核中开启了data cache,而我们应用中并不需要开启,因此通过软件关闭。只需要将第31位置1,cache即可关闭。
uart.h中主要是串口通信相关的函数变量结构体,便于管理和统一。
#ifndef UART_H_
#define UART_H_
#include".../inc/sopc.h"
#define BUFFER_SIZE
typedef struct
{
unsigned int receive_flag;
unsigned int receive_count;
unsigned char receive_buffer[BUFFER_SIZE];
int(*send_byte)(unsigned char data);
void(*send_string)(unsigned int len, unsigned char *str);
int(*init)(void);
unsigned int(*baudrate)(unsigned int baudrate);
}UART_T;
extern UART_T uart; //extern声明变量,表明此变量若在别处定义,需要此处引用
#endif
2)编写串口通信需要的函数
//结构体初始化
UART_T uart={
.receive_flag=0,
.receive_count=0,
.send_byte=uart_send_byte,
.send_string=uart_send_string,
.init=uart_init,
.baudrate=set_baudrate};
int uart_send_byte(unsigned char data)
{
UART->TXDATA.BITS.TRANSMIT_DATA=data; //指针指向,必须用->,实体里面的成员用.
//等待TRDY从0变成1
while(UART->STATUS.BITS.TRDY==0); //RRDY=0,txdata register is full,not ready。 =1,txdata register is empty,readey
return 0;
}
void uart_send_string(unsigned int len, unsigned char * str)
{
while(len--)
{
uart_send_byte(*str++);
}
}
int set_baudrate(unsigned int baudrate)
{
UART->DIVISOR.WORD=(unsigned int)(ALT_CPU_FREQ)/(baudrate-1);
return 0;
}
int uart_init(void)
{
// 寄存器初始化
UART->CONTROL.BITS.IBRK=1; // enable interrupt for every detect 接收中断
UART->STATUS.WORD=0; //清空状态
//波特率设置
set_baudrate(115200);
// 中断初始化 包含中断使能,中断注册表
alt_ic__isr_register(UART_IRQ_INTERRUPT_CONTROLLER_ID,UART_IRQ,uart_ISR,NULL,0x0);
return 0;
}
static void uart_ISR(void)
{
//等待RRDT为1,当为1说明接收的数据已经存入到rxdata寄存器中
while(UART->STATUS.BITS.RRDY==0);
//将接收的放在寄存器中的数据存放到指定的buffer中
uart.receive_buffer[uart.receive_count++]=UART->RXDATA.BITS.RECEIVE_DATA;
//判断是否接收结束,以\n标志结束
if(uart.receive_buffer[uart.receive_count-1]=='\n')
{
uart.receive_buffer[uart.receive_count]='\0';
uart.receive_count=0;
uart.receive_flag=1;
}
}
中断的产生:这里选择IBRK作为中断。通过检测接口是否接收到低电平,也就是起始位。如果接收到,产生中断。
中断函数:产生中断后,需要先确认rxdata寄存中是否已准备好数据,如果准备好了,那么将数据寄存,然后将标志位置1,如果没有,不可以寄存数据,可能导致寄存的数据是错误的。
如上图所示,发送数据,需要往txdata 寄存器中写入数据,并且需要看状态寄存器中的TRDY位,如下图,要写一个新的数据给txdata 寄存器,TRDY必须=1。
波特率的设置如下:
3)main函数
int main(void)
{
//初始化字符串BUFFER
unsigned char buffer[50]="HELLO, FPGA\n";
//串口初始化
uart.init();
//检测是否接收到PC端发来的数据
while(1)
{
//若串口收到PC的数据,执行中断函数,那么将存在buffer中的值寄存
if(uart.receive_flag)
{
memset(buffer,0,50); //buffer清零
strcpy(buffer,uart.receive_buffer);//复制
uart.receive_flag=0;
}
//将寄存的值发送给PC
uart.send_string(sizeof(buffer),buffer);
usleep(500000);
}
return 0;
}
方式2
方式1是通过自己将UART的寄存器封装成结构体,然后自己构建串口通讯需要的函数。但是在eclipse的模板中,也直接提供了相关的函数, 可以直接调用来实现我们的功能。关于串口的寄存器在altera_avalon_uart_regs.h中,里面还包含各种宏定义。
例如要实现串口中断函数,如下,直接对寄存器进行操作。