Menu Close

RISC-V UART中断设计与应用(2)UART1 CPU实现和工程头文件

1.UART CPU实现

 

相关参考文章:

RISC-V教学教案

RISC-V UART1模块除了添加连接总线RIB读写寄存器来配置一些参数(例如奇偶校检位)外,和一般的UART的物理层(physical layer)一样,也是采用8倍波特率,中心采样的方法,详细说明参见异步串行收发(UART)协议详解(2)–波特率

因为UART两端的速度不同,例如FPGA系统时钟频率为50 MHz,而UART常用波特率为115200 Hz,所以在发送和接收端都增加了FIFO。具体的UART1模块设计见图1。这里rx和tx分别表示receive,接收和transmit,发送。phy指物理层,在该模块里,只完成发送和接收功能。buffer模块将物理层和FIFO连接起来。transceiver模块将寄存器配置的参数分别传递给接收和发送模块。RIB_UART1模块连接总线,完成UART1相关寄存器读写。

可以看到,模块的等级制结构使得模块之间的联系清晰明了。如果之后需要扩展另外一个UART,在当前UART模块基础上添加非常的方便,只需要小部分修改,比如总线上的地址片选,其他的部分可直接拿来用。

%title插图%num

图1 UART1 模块设计

在总线中添加UART1的地址和片选如图2所示,UART1的基地址参数如图3所示。

%title插图%num

图2 添加总线中的片选

 

%title插图%num

图3 添加UART1基地址

 

UART1中断也是由外部平台级中断处理器(PLIC)统一处理,所以UART1也被分配了一个唯一的PLIC ID,如图4所示。

%title插图%num

图4 部分PLIC ID分配

 

图5所示为PLIC中的UART1中断请求。

%title插图%num

图5 PLIC 中UART1的中断请求

 

2.UART1中断软件工程

在之前介绍过的PLIC软件搭建,以及外部中断源中的GPIO和PWM软件工程的基础上,这里将会只说明和之前相比新添加的部分。之前的PLIC软件工程详情见RISC-V教学教案(点击这里)中的18-21节,如图6所示。

%title插图%num

图6 RISC-V教学教案中PLIC相关的文章

接下来会介绍UART1工程用到的头文件:platform.h和fii_uart1.h。

  • Platform.h

因为在设置中断时,不仅要设置UART1的中断相关寄存器,还需要在PLIC层也打开中断和设置优先级,所以在platform.h中需要添加UART1的偏移量,这里的UART1偏移量是根据地址图上对不同外部中断源的定义,如图4所示。

 

//=============================UART definition============================

#define INT_UART0_BASE 4

#define INT_UART1_BASE 5

#define INT_UART2_BASE 6

#define INT_UART3_BASE 7

//============================UART definition END==========================

 

  • Fii_uart1.h

Fii_uart1.h中定义了关于UART1基地址的偏移量,以及相关的参数和UART1中断初始化函数,具体解释见下列代码。

 

#include "fii_types.h"

//UART1基地址偏移量

#define UART1_OFFSET 0X1000

#define UART1_REG(offset) (*(volatile unsigned int *)(UART_BASE + UART1_OFFSET + offset)) //UART1相对于UART基地址的偏移量

#define UART1_TX_DATA 0X00              //发送数据寄存器偏移量

#define UART1_RX_DATA 0X04              //接收数据寄存器偏移量

#define UART1_TX_CTRL 0X08              //发送控制寄存器偏移量

#define UART1_RX_CTRL 0X0C              //接收控制寄存器偏移量

#define UART1_IE 0X10                   //中断使能寄存器偏移量

#define UART1_IP 0X14                   //中断悬挂寄存器偏移量

#define UART1_IC 0X18                   //中断清除寄存器偏移量

#define UART1_DIV 0X1C                  //波特率分频寄存器偏移量

#define UART1_LCR 0X20                  //奇偶校检寄存器偏移量

#define UART1_VER 0X24                  //版本寄存器偏移量

#define UART1_TX_FIFO_FULL 0X80000000                //发送数据寄存器中FIFO空的屏蔽

#define UART1_RX_FIFO_EMPTY 0X80000000               //接收数据寄存器中FIFO满的屏蔽

#define UART1_DATA_MASK 0XFF            //发送和接收数据寄存器中8 bit数据的屏蔽

#define UART1_TXEN 0X1                  //发送控制寄存器中发送使能的屏蔽

#define UART1_RXEN 0X1                  //接收控制寄存器中接收使能的屏蔽

//UART1中断使能的相对位移

#define UART1_TXWM_MASK 0X0

#define UART1_RXWM_MASK 0X1

#define UART1_PERROR_MASK 0X2

//用于设置LCR寄存器中奇偶校检的屏蔽

#define NO_PARITY 0X0

#define ODD_PARITY 0X28

#define EVEN_PARITY 0X18

#define MARK_PARITY 0X8

#define SPACE_PARITY 0X38

//div寄存器波特率计算公式:

// ( 50,000,000 / 目标波特率 ) - 1

//比如 115200 Hz波特率,50 MHz时钟频率, 50_000_000/115200 = 434, div = 434 - 1 = 433 = 0x1b1

//这里列举了一些常用的波特率,可以直接写入div寄存器

#define BAUD_RATE_115200 0X1B1

#define BAUD_RATE_57600 0X363

#define BAUD_RATE_38400 0X515

#define BAUD_RATE_19200 0XA2B

#define BAUD_RATE_14400 0XD8F

#define BAUD_RATE_9600 0X1457

#define BAUD_RATE_1200 0XA2C2

//=================================================================================

//枚举定义中断使能

typedef enum {

UART1_IRQ_DIS = 0,

UART1_IRQ_EN

} E_UART1_IRQ_SW;

//枚举定义数据停止位

typedef enum {

UART1_NSTOP_1 = 0,

UART1_NSTOP_2

} E_UART1_NSTOP;

//将TXCTRL和RXCTRL寄存器的比特位分开来,方便直接访问发送/接收中断水印阈值

typedef union

{

struct

{

unsigned int reserved1 :16;

unsigned int rxwm_bit :3;

unsigned int reserved2 :13;

}bit;

unsigned int rxctrl_reg;

}E_RXCTRL;

typedef union

{

struct

{

unsigned int reserved1 :16;

unsigned int txwm_bit :3;

unsigned int reserved2 :13;

}bit;

unsigned int txctrl_reg;

}E_TXCTRL;

//=================================================================================

//声明UART1_IRQ_register函数,用于UART1中断初始化

void UART1_IRQ_register( u32_t uart1_baud_rate, u32_t uart1_parity, u32_t irq_mask,

E_UART1_NSTOP nstop, E_RXCTRL rxwm, E_TXCTRL txwm, E_UART1_IRQ_SW uart1_sw);

//===============================

 

这里需要补充一下关于union和struct的知识。因为txctrl和rxctrl中水印阈值占3个bit,所以在C语言中按每比特去访问变量需要用到struct,如下面的代码示例。

struct { 

unsigned int reserved1 :16;

unsigned int txwm_bit :3;

unsigned int reserved2 :13; 

}bit;

可以看到变量bit被分成了三个部分,按照顺序排列,bit 0-15被保留,bit 19-31也被保留,而bit 16-18,中断水印阈值可以被单独访问。之所以在struct外面增加一个union,是因为union可以在同一个内存地址存储不同的数据类型。加上union后,既可以访问单独的3 bit数据,也可以一起访问整个变量,代码如下所示:

typedef union { 

struct { 

unsigned int reserved1 :16; 

unsigned int txwm_bit :3; 

unsigned int reserved2 :13; 

}bit; 

unsigned int txctrl_reg; 

}E_TXCTRL;

可以看到txctrl_reg是一个32位的变量,txwm_bit是一个只有3 bit的数据。在访问时,可以通过如下的代码:

E_TXCTRL txwm;

txwm.bit.txwm_bit = 0x3;

UART1_REG(UART1_TX_CTRL) = txwm.txctrl_reg;

这里的例子是将txwm的bit 16-18 设置为3,然后其他保留区域不变,将其整体赋值给txctrl寄存器。这里E_TXCTRL是用typedef定义unsigned int的别名。

 

 

附件下载

Posted in C语言, RISC-V, RISC-V 外设, RISC-V 教案, Verilog, 应用开发, 教材与教案, 文章, 编程语言

发表评论

相关链接