红圈1处为波特率,我们设置为115200;红圈2处是是否允许通过软件改变波特率,我们选中,便是允许,这样我们就可以通过软件来随时更改波特率,如果软件不设置,默认值就是上面设置的115200;
system.h中uart部分
/* * uart configuration * */ #define UART_NAME "/dev/uart" #define UART_TYPE "altera_avalon_uart" #define UART_BASE 0x00001800 #define UART_SPAN 32 #define UART_IRQ 3 #define UART_IRQ_INTERRUPT_CONTROLLER_ID 0 #define UART_BAUD 115200 #define UART_DATA_BITS 8 #define UART_FIXED_BAUD 0 #define UART_PARITY 'N' #define UART_STOP_BITS 1 #define UART_SYNC_REG_DEPTH 2 #define UART_USE_CTS_RTS 0 #define UART_USE_EOP_REGISTER 0 #define UART_SIM_TRUE_BAUD 0 #define UART_SIM_CHAR_STREAM "" #define UART_RELATIVEPATH 0 #define UART_FREQ 100000000 #define ALT_MODULE_CLASS_uart altera_avalon_uart
#ifndef SOPC_H_ #define SOPC_H_ /*------------------------------------------------------------------------- * Include *------------------------------------------------------------------------*/ #include "system.h" /*------------------------------------------------------------------------- * Define *------------------------------------------------------------------------*/ #define _UART /*------------------------------------------------------------------------- * Peripheral registers structures *------------------------------------------------------------------------*/ /*--------------------------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 int WORD; }DIVISOR; }UART_ST; /*------------------------------------------------------------------- * Peripheral declaration *------------------------------------------------------------------*/ #ifdef _UART #define UART ((UART_ST *) UART_BASE) #endif /*_UART */ #endif /*SOPC_H_*/
这个结构体中包括5个共用体,这5个共用体对应RS232的5个寄存器,我们来看看这5个寄存器,下图所示,这个图来自《n2cpu_Embedded Peripherals.pdf》
我们通过上表可以看到,UART包括6个寄存器(由于最后一个寄存器一般不用,所以建立的结构体中没有加入它),假设基地址为0x00的话,那么他们的地址分别为0x00,0x01,0x02,0x03,0x04,0x05。也就是说,各个寄存器之间是存在顺序的。那么,在我们建立结构体过程中也要注意他们的顺序问题
第一, 整个结构体由5个共用体组成,共同体的顺序是由寄存器的偏移量决定的,这一点前面已经有所叙述。
第二, 每个共用体由一个结构体和一个volatile unsigned long int型的变量组成。
第三, 共用体中的结构体由位域构成,位域中的内容也是存在顺序的,这个顺序是由寄存器的结构决定,而且是由低到高排列。其中,NC表示该位为空位或保留,不能对其进行操作。
通过大家的反馈,除了语法问题以外,有两个问题需要说明一下:
1. 为什么里面的变量都定义成unsigned long int?
首先需要说明一点,在NIOS II中,unsigned long int是32位,跟unsigned int是一样的,unsigned long long int才是64位的。
有人会问,在寄存器的表格中,寄存器的位数是0到15的,也就是16位,那你为什么定义成32位的呢?其实,这个问题涉及到了NIOS II的地址对齐问题,它是属于硬件构架的范畴。
当系统中存在数据宽度不匹配的主从端口时就要考虑地址对齐的问题。地址对齐分为两类,一类是静态地址对齐,另一类就是动态地址对齐。一般来说存储器外设使用动态地址对齐,而寄存器外设使用静态地址对齐,之所以是这样,是由动态地址对齐和静态地址对齐的特点决定的,在静态地址对齐方式下,主端单次传输对应从端口的一次传输,而在动态地址对齐方式下,一个主端口读传输,则要引起多次从端口读传输。
我们要将寄存器定义为unsigned long int类型,就跟这个静态地址对齐有关系了。现在我们是UART端口16位,而NIOS II主端口32位的情况,在这种情况下,NIOS II主端口与16位UART端口进行数据传输时,只有32位的低16位有效,但是高16位也占用了地址空间,也就是说,UART端口的16位实际上是占用了32位的。假设我们现在的基地址是0X00,那么6个寄存器他们相对基地址的偏移分别为0X00,0X01,0X02,0X03,0X04,0X05;那么,从主端口看,这6个寄存器的地址分别为0X00,0X04,0X08,0X0C,0X10,0X14,而不是0X00,0X01,0X02,0X03,0X04,0X05,也不是0X00,0X02,0X06,0X08,0X0A,0XC,这一点大家要特别注意。
2. 为什么要建立这样一个共用体呢,又有位域结构体又有一个volatile unsigned long int WORD变量,WORD有啥用呢?
共用体的特点就是其中的成员占用同一个存储空间。也就说,由位域组成的结构体跟WORD是占用同一存储空间,而且他们都是volatile unsigned long int类型,那么,结构体中的每一个位域成员都对应WORD的一个位。当我们需要单独处理一个位的时候,我们就可以用位域,如下所示
RS232->CONTROL.BITS.IRRDY=1;//接收准备好中断使能 |
如果我们想要对状态寄存器整体清零呢,我们就可以用到WORD了,如下所示
RS232->STATUS.WORD=0;//清除状态寄存器 |
对于其他的寄存器都是一样的,在这里不再重复了。
这个图中的(1)有一个说明,就是说第7,8位根据设置的数据位有所改变,我们设置数据位8位,所以7,8位与前6为性质相同。
与之前讲的PIO的结构体类似,这个结构体的内容是按上图的寄存器顺序来定义的,(因为endofpacket没用到,所以在结构中没有定义)这样在操作过程中就可以实现相应的偏移量(offset)。
在这个结构体中,我们嵌套了5个共有体,在共用体中,我们又使用了结构体和位域。头一次看的一定很头晕。其实,我们这样做的目的就是想对寄存器的每一位进行单独的控制,同时也可以实现这个寄存器的整体控制。具体应用,我们在下面的程序中会应用到。
有了上面来的结构体以后,我们需要定义一个宏,跟PIO的类似。
接下来,我们要在inc下建立uart.h文件
#ifndef UART_H_ #define UART_H_ /*----------------------------------------------------------------------------- * Include *-----------------------------------------------------------------------------*/ #include "../inc/sopc.h" /*----------------------------------------------------------------------------- * Define *-----------------------------------------------------------------------------*/ #define BUFFER_SIZE 200 /*------------------------------------------------------------------------------ * Struct *-----------------------------------------------------------------------------*/ 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); int (* baudrate)(unsigned int baudrate); }UART_T; extern UART_T uart; #endif /*UART_H_*/
在上面的代码中,结构体UART_T很重要,它是模拟面向对象的一种编程思想,也是我之前说的一种很重要的编程方式。我们将与UART有关系的所有函数、变量都打包在一起,对其他函数来说,它们只能看到uart这个结构体,而里面的单独部分都是不可见的。希望大家可以好好体会其中的思想,对大家的编程一定会有很大的好处。
下面,我们要开始写RS232的驱动了,首先我们要在driver下面建立一个.c文件,命名为uart.c
/*-------------------------------------------------------------------------------------- * Include *-------------------------------------------------------------------------------------*/ #include "sys/alt_irq.h" #include <stdlib.h> #include <stdio.h> #include "../inc/uart.h" #include "../inc/sopc.h" /*-------------------------------------------------------------------------------------- * Function Prototype *------------------------------------------------------------------------------------*/ static int uart_send_byte(unsigned char data); static void uart_send_string(unsigned int len, unsigned char *str); static int uart_init(void); static void uart_ISR(void); static int set_baudrate(unsigned int baudrate); /*-------------------------------------------------------------------------------------- * Struct initialize *------------------------------------------------------------------------------------*/ 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 }; /* * === FUNCTION ====================================================================== * Name: uart_send_byte * Description: * ===================================================================================== */ int uart_send_byte(unsigned char data) { //将发送的数据放到发送数据缓冲区内,等待状态寄存器TRDY置1,当TRDY置1,说明接收完毕
UART->TXDATA.BITS.TRANSMIT_DATA = data; while(!UART->STATUS.BITS.TRDY); return 0; } /* * === FUNCTION ====================================================================== * Name: uart_send_string * Description: * ===================================================================================== */ void uart_send_string(unsigned int len, unsigned char *str) { while(len--) { uart_send_byte(*str++); } } /* * === FUNCTION ====================================================================== * Name: uart_init * Description: * ===================================================================================== */ int uart_init(void) { set_baudrate(115200); UART->CONTROL.BITS.IRRDY=1; UART->STATUS.WORD=0; alt_irq_register(UART_IRQ, NULL, uart_ISR); return 0; } /* * === FUNCTION ====================================================================== * Name: uart_ISR * Description: * ===================================================================================== */ static void uart_ISR(void) { //等待状态寄存器的接收数据状态位rrdy,当rrdy位为1时,说明新接收的值传输到接收数据寄存器 while(!(UART->STATUS.BITS.RRDY)); //receive_buffer为我们通过栈的方式在内存中开设的内存快,将接收数据寄存器中数据放在这个内存中 uart.receive_buffer[uart.receive_count++] = UART->RXDATA.BITS.RECEIVE_DATA; //当接收到的数据的最后一位为\n(回车符)时,进入if语句,也就是说,\n作为结束标志符,每次发送数据后,要加一个回车符作为结束符 if(uart.receive_buffer[uart.receive_count-1]=='\n'){ uart.receive_buffer[uart.receive_count]='\0'; uart_send_string(uart.receive_count,uart.receive_buffer); uart.receive_count=0; uart.receive_flag=1; } } /* * === FUNCTION ====================================================================== * Name: set_baudrate * Description: * ===================================================================================== */ int set_baudrate(unsigned int baudrate) { //设置波特率有公式:波特率=时钟频率/(divisor+1),转换以后就是下面的 UART->DIVISOR.WORD=(unsigned int)(ALT_CPU_FREQ/baudrate+0.5); return 0; }
main.c
/*----------------------------------------------------------------------------- * Include *-----------------------------------------------------------------------------*/ #include "system.h" #include "sys/alt_irq.h" #include <unistd.h> #include <stdio.h> #include <string.h> #include "../inc/uart.h" #include "../inc/sopc.h" /* * === FUNCTION ====================================================================== * Name: main * Description: * ===================================================================================== */ int main(void) { unsigned char buffer[50]="Hello FPGA!\n"; uart.init(); while(1){ if(uart.receive_flag){ memset(buffer,0,50);// clear buffer strcpy(buffer,uart.receive_buffer);//copy uart.receive_buffer to buffer uart.receive_flag = 0;//clear flags } uart.send_string(sizeof(buffer),buffer); usleep(500000); } return 0; }