跳至主要内容

【转】ARM9硬件接口学习之五 UART

在没有引入UART之前,由于没有OS,我们写程序烧到开发板内执行后,并不能在程序的运行过程中打印一些提示信息告诉我们程序究竟运行了如何或者提供接口让用户去控制程序的运行路径,最后也只能通过物理现象去判断程序是否执行成功。

 

使用串口我们便可以实现开发板最基本的数据的发送和接收,实现同开发板的交互,控制程序的运行,并且在程序运行中打印出一些信息进行debug。实际上bootloaderkernel的控制台(nfs模式)都是通过uart实现的。由此我们可以发现uart在实际开发中是非常重要的。

 

1. s3c2410串口基础

S3C2410A UART详细的规格说明请参考s3c2410datasheet。这里我只分析和试验相关的部分重点。

1) S3c2410uart默认使用的系统时钟是PCLK

这和计算uart的波特率有关。

之前在CLOCK的学习中我也提到过,具体可以看里面那张clock block digram.

http://blog.chinaunix.net/u2/60011/showart_474430.html

 

2) UART的功能模块及数据传输流程

每一个uart都包含一个波特率发生器(Baudrate Generator),发送器(Transmitter),接收器(Receiver)以及一个控制逻辑(Control Unit)。如下图:11-1

波特率发生器使用的时钟可以为PCLK(默认)或者UEXTCLK(主要是为了达到更高的波特率,默认使用PCLK最高为230.4k bps)。发送器和接受器分别包括一个16-byteFIFO以及一个数据移位器(data shifter)。数据通过发送引脚(TxDn)和接收引脚(RxDn)进行发送和接收。

 

发送数据时,CPU通过内部总线将要发送的数据写入Transmit buffer,对程序员来讲即将数据写入Transmit Holding Register(若使用FIFO Mode也是写入这个寄存器,硬件内部会自动判断)。然后Transmitter按照Buad-rate Generator产生的波特率将Transmit Buffer内的数据移入Transmit Shifter, 最后通过TXDn引脚发送出去。

 

接收数据时,接收引脚(RxDn)按一定波特率进行数据接收进来存放在Receive Shifter然后再移入Receive Buffer。对程序员来讲即通过Receive Holding Register读取接收到的数据(类似发送,不管是否使用FIFO Mode都是读该寄存器获取接收到的数据)

 

Transmit Holding RegisterReceive Holding Register都是8 bit大小的寄存器,即每次可读写一个字节数据。

 

3) 波特率的计算

波特率时钟主要是用来提供串口数据发送和接收时所需要的时钟信号。

计算方法为源时钟(默认为PCLK)除以16以及一个16位分频因子(divisor)。分频因子的值存储在baudrate divisor register (UBRDIVn)内,由用户指定。

通常我们计算波特率的方法为根据想要的波特率反过来计算divisor, 然后将该值写入Divisor Register(UBRDIVn)寄存器内。公式如下:

UBRDIVn = (int)(PCLK/(bps x 16) ) -1

Bps为我们需要设置的波特率,比如115200

 

2. s3c2410串口实验

实验代码很简单,非常适合串口编程入门。

内容为:通过串口打印出一行信息提示用户输入一个字符。若用户输入'e'即退出程序。若输入其他字符则重复尝试。

 

下面具体分析:(部分内容引用自《S3C2410完全开发流程》,这里感谢其作者的贡献)

UART的寄存器有11X3(3UART)之多,我们选最简单的方法来进行本实验,用到的寄存器也有8个。不过初始化就用去了5个寄存器,剩下的3个用于接收、发送数据。如此一来,操作UART倒也不复杂。本板使用UART0

1) 初始化:

a. 把使用到的引脚GPH2GPH3定义为TXD0RXD0

GPHCON |= 0xa0; //GPH2,GPH3 set as TXD0,RXD0

GPHUP = 0x0c;   //GPH2,GPH3内部上拉

bULCON0 ( UART channel 0 line control register ):设为0x03

此值含义为:8个数据位,1个停止位,无校验,正常操作模式。

cUCON0 (UART channel 0 control register ):设为0x05

除了位[3:0],其他位都使用默认值。位[3:0]=0b0101表示:发送、接收都使用"中断或查询方式"――本实验使用查询查询方式。

dUFCON0 (UART channel 0 FIFO control register ):设为0x00

每个UART内部都有一个16字节的发送FIFO和接收FIFO,但是本实验不使用FIFO,设为默认值0

eUMCON0 (UART channel 0 Modem control register ):设为0x00

本实验不使用流控,设为默认值0

fUBRDIV0 ( R/W Baud rate divisior register 0 ):设为27

UBRDIV0 = 27;   //波特率为115200

本实验使用PLLPCLK=50MHz,设置波特率为115200,则由公式

UBRDIVn = (int)(PCLK / (bps x 16) ) �1

可以计算得UBRDIV0 = 27,请使用S3C2410数据手册第314页的误差公式验算一下此波特率是否在可容忍的误差范围之内,如果不在,则需要更换另一个波特率(本实验使用的115200是符合的)

 

2) 发送数据:

aUTRSTAT0 ( UART channel 0 Tx/Rx status register )

[2]:无数据发送时,自动设为1。当我们要使用串口发送数据时,先读此位以判断是否有数据正在占用发送口。

[1]:发送FIFO是否为空,本实验未用此位

[0]:接收缓冲区是否有数据,若有,此位设为1。本实验中,需要不断查询此位一判断是否有数据已经被接收。

bUTXH0 (UART channel 0 transmit buffer register )

把要发送的数据写入此寄存器。

 

3) 接收数据:

aUTRSTAT0:如上描述,我们用到位[0]

bURXH0 (UART channel 0 receive buffer register )

当查询到UTRSTAT0 [0]=1时,读此寄存器获得串口接收到的数据。

 

4) 实验源码

/* main.c */
#include "uart.h"
#include "clock.h"
#include "watchdog.h"

int Main(void)
{
    char key = ' ';
    clock_init(); //初始化时钟
    uart_init(); //初始化串口
    close_watchdog();


    uart_send("uart communication success!\r\n");
    
    while(1)
    {
        uart_send("If you want to quit ,please pess 'e'\r\n");
        key = uart_get();
        if (key == 'e')
        {
            uart_send ("you pressed 'e' and you'll quit!\r\n");
            break;
        }
        else
         {
            uart_send("you pressed ");
            uart_send(&key);
            uart_send(",retry!\r\n");
        }
    }
    uart_send("the program exited by user!\r\n");
    return 0;
}

 

注:这里关闭watchdog的目的主要是为了防止系统不断重启干扰uart的实验过程(watchdog默认是打开的)。

 

下面是串口相关部分源码:

void uart_init(void)
{
    ULCON0 = 0x03;     //8N1
    UCON0 = 0x005; //中断或查询方式
    UFCON0 = 0x00;    //不使用FIFO
    UMCON0 = 0x00;    //不使用流控
    UBRDIV0 = 27;    //波特率为115200
    GPHCON |= 0xa0; //GPH2,GPH3 set as TXD0,RXD0
    GPHUP = 0x0c;    //GPH2,GPH3内部上拉
}
void uart_send(char * c)
{
    for (; *c != '\0'; c++)
    {
        while(!(UTRSTAT0 & TXD0READY)) ;     //不断查询,直到可以发送数据
        UTXH0 = *c ;                        //发送数据
    }
}
unsigned char uart_get(void)
{
    while(!(UTRSTAT0 & RXD0READY)) ;     //不断查询,直到接收到了数据
    return URXH0;                        //返回接收到的数据
}

 

评论

此博客中的热门博文

【转】VxWorks中的地址映射

在运用嵌入式系统VxWorks和MPC860进行通信系统设计开发时,会遇到一个映射地址不能访问的问题。 缺省情况下,VxWorks系统已经进行了如下地址的映射:   memory地址、bcsr(Board Control and Status)地址、PC_BASE_ADRS(PCMCIA)地址、Internal Memory地址、rom(Flach memory)地址等,但是当你的硬件开发中要加上别的外设时,如(falsh、dsp、FPGA等),对这些外设的访问也是通过地址形式进行读写,如果你没有加相应的地址映射,那么是无法访问这些外设的。   和VxWorks缺省地址映射类似,你也可以进行相应的地址映射。   如下是地址映射原理及实现:   1、 地址映射结构 在Tornado\target\h\vmLib.h文件中 typedef struct phys_mem_desc { void *virtualAddr; void *physicalAddr; UINT len; UINT initialStateMask; /* mask parameter to vmStateSet */ UINT initialState; /* state parameter to vmStateSet */ } PHYS_MEM_DESC; virtualAddr:你要映射的虚拟地址 physicalAddr:硬件设计时定义的实际物理地址 len;要进行映射的地址长度 initialStateMask:可以初始化的地址状态: 有如下状态: #define VM_STATE_MASK_VALID 0x03 #define VM_STATE_MASK_WRITABLE 0x0c #define VM_STATE_MASK_CACHEABLE 0x30 #define VM_STATE_MASK_MEM_COHERENCY 0x40 #define VM_STATE_MASK_GUARDED 0x80 不同的CPU芯片类型还有其特殊状态 initialState:实际初始化的地址状态: 有如下状态: #define VM_STATE_VALID 0x01 #define VM_STATE_VALID_NOT 0x00 #define VM_STATE_WRITA

【转】多迷人Gtkmm啊

前边已经说过用glade设计界面然后动态装载,接下来再来看看怎么改变程序的皮肤(主题)     首先从 http://art.gnome.org/themes/gtk2 下载喜欢的主题,从压缩包里提取gtk-2.0文件夹让它和我们下边代码生成的可执行文件放在同一个目录下,这里我下载的的 http://art.gnome.org/download/themes/gtk2/1317/GTK2-CillopMidnite.tar.gz     然后用glade设计界面,命名为main.glade,一会让它和我们下边代码生成的可执行程序放在同一个目录下边     然后开始写代码如下: //main.cc #include <gtkmm.h> #include <libglademm/xml.h> int main(int argc, char *argv[]) {     Gtk::Main kit(argc,argv);         Gtk::Window *pWnd;        gtk_rc_parse("E:\\theme-viewer\\themes\\gtk-2.0\\gtkrc");       Glib::RefPtr<Gnome::Glade::Xml> refXml;     try     {         refXml = Gnome::Glade::Xml::create("main.glade");     }     catch(const Gnome::Glade::XmlError& ex)     {         Gtk::MessageDialog dialog("Load glade file failed!", false,       \                                   Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK);         dialog.run();               return 1;     }         refXml->get_widget("main", pWnd);     if(pW

【转】https客户端的实现(libcurl)

一、              概念 1.         为什么要使用libcurl 1)        作为http的客户端,可以直接用socket连接服务器,然后对到的数据进行http解析,但要分析协议头,实现代理…这样太麻烦了。 2)        libcurl是一个开源的客户端url传输库,支持FTP,FTPS,TFTP,HTTP,HTTPS,GOPHER,TELNET,DICT,FILE和LDAP,支持Windows,Unix,Linux等平台,简单易用,且库文件占用空间不到200K 2.         get和post方式 客户端在http连接时向服务提交数据的方式分为get和post两种 1)        Get方式将所要传输的数据附在网址后面,然后一起送达服务器,它的优点是效率比较高;缺点是安全性差、数据不超过1024个字符、必须是7位的ASCII编码;查询时经常用此方法。 2)        Post通过Http post处理发送数据,它的优点是安全性较强、支持数据量大、支持字符多;缺点是效率相对低;编辑修改时多使用此方法。 3.         cookie与session 1)        cookie cookie是发送到客户浏览器的文本串句柄,并保存在客户机硬盘上,可以用来在某个Web站点会话之间持久地保持数据。cookie在客户端。 2)        session session是访问者从到达某个特定主页到离开为止的那段时间。每一访问者都会单独获得一个session,实现站点多个用户之间在所有页面中共享信息。session在服务器上。 3)        libcurl中使用cookie 保存cookie, 使之后的链接与此链接使用相同的cookie a)         在关闭链接的时候把cookie写入指定的文件 curl_easy_setopt(curl, CURLOPT_COOKIEJAR, "/tmp/cookie.txt"); b)        取用现在有的cookie,而不重新得到cookie curl_easy_setopt(curl, CURLOPT_COOKIEFILE, "/tmp/cookie.txt"); b)        ht