随笔 - 60, 文章 - 0, 评论 - 197, 引用 - 0
数据加载中……

学习 ARM 系列 -- FS2410 开发板上的串口通信编程

一、目的
   串口通信我们并不陌生,我们经常用串口来进行数据传输,可并不清楚它是如何工作
的。那这一节我们就来揭开 ARM S3c2410 UART(Universal Asynchronous Receiver and
Transmitter) 串口通信的神秘面纱。


二、代码
   我们先来分析文件 crt0.s
  
   @ 文件 crt0.s
   @ 作用:设置堆栈指针
   .text
   .global _start
   _start:
    ldr sp, =1024*4
    bl main
   halt_loop:
    b halt_loop
  
   你可能会有疑问,这个汇编文件有什么用?呵呵,这是因为我们的串口通信代码要用 C
编写(用汇编可读性太差了)。可这又和这个 crt0.s 有什么关系呢?这得从 C 语言程序的
编译说起。C 语言程序执行的第一条指令并不在 main 函数里。当生成一个 C 语言程序时
编译器总是在我们的代码前加一段固定的代码--crt0.o,它是编译器自带的一个文件,用来
设置 C 程序的堆栈等,然后调用 main 函数。可惜在我们的裸板上它自带的 crt0.o 的代
码是不能运行的,我们得自己动手写,这就是为什么要有 crt0.s 这个文件。稍后你将看到,
这个 crt0.s 被编译成我们自己的 crt0.o 文件。


   /* 头文件 serl.h
    * 作用:定义相关寄存器、UART 初始化函数声明、串口读写函数的声明
    */
   #ifndef __SERL_H__
   #define __SERL_H__
  
   #define GPHCON  (*(volatile unsigned long *)0x56000070)
  
   /* PORT PULL-UP REGISTER*/
   #define GPHUP   (*(volatile unsigned long *)0x56000078)
  
   /* UART FIFO control register 0*/
   #define UFCON0  (*(volatile unsigned long *)0x50000008)
  
   /* UART line control register 0*/
   #define ULCON0  (*(volatile unsigned long *)0x50000000)
  
   /* UART control register 0*/
   #define UCON0   (*(volatile unsigned long *)0x50000004)
  
   /* UART modem control register 0*/
   #define UMCON0  (*(volatile unsigned long *)0x5000000C)
  
   /* UART baud rate divisor register 0*/
   #define UBRDIV0 (*(volatile unsigned long *)0x50000028)
  
   /* UART TX/RX status register 0*/
   #define UTRSTAT0 (*(volatile unsigned long *)0x50000010)
  
   /* UART transmit buffer register 0*/
   #define UTXH0 (*(volatile unsigned char *)0x50000020)
  
   /* UART receive buffer register 0*/
   #define URXH0 (*(volatile unsigned char *)0x50000024)
  
  
   #define TXD0_READY 0x2
   #define RXD0_READY 0x1
  
   void init_uart();
   void putc(unsigned char ch);
   unsigned char getc();
  
   #endif


   /* 文件 serl.c*/
   #include "serl.h"
  
   void init_uart() {
     GPHCON |= 0xa0; /* GPH2, GPH3 used as RXD0, TXD0*/
     GPHUP = 0x0c;   /* GPH2, GPH3 poll-up */
  
     ULCON0 = 0x03;  /* normal mode, no parity, one stop bit, 8-bit*/
     UCON0 = 0x05;   /* Loopback mode*/
     UFCON0 = 0x00;  /* not use FIFO*/
     UMCON0 = 0x00;  /* disable flow control*/
     UBRDIV0 = 12;   /* baud rate 57600*/
   }
  
   void putc(unsigned char ch) {
     while (!(UTRSTAT0 & TXD0_READY));
     UTXH0 = ch;
   }
  
   unsigned char getc(){
     while (! (UTRSTAT0 & RXD0_READY));
     return URXH0;
   }
  

我们选用最简单的方法,用 UART0 进行实验,用到的寄存器有8个多,初始化用去5
个,余下的3个用于接收、发送数据。初始化设置的代码说明如下:
   1. GPHCON 的 GPH2、GPH3用控制接收数据寄存器 RXD0 和发送数据寄存器 TXD0
      手册中GPH2、GPH3描述如下:

GPHCON     Bit         Description
GPH3 [7:6] 00 = Input    01 = Output   
10 = RXD0  11 = reserved
GPH2              [5:4]  00 = Input     01 = Output
10 = TXD0   11 = Reserved
                                                                                                              
      所以
        GPHCON |= 0xa0
        GPHUP  |= 0x0c (上拉)

   2. ULCON0 设置为 0x03, 含义是正常操作模式、无校验、停止位1、8个数据位
   3. UCON0 设置为 0x05 表示发送、接收数据都使用查询方式
   4. UFCON0 设置为 0x00 为不使用 FIFO (每个UART内部都有一个16字节的发送和接收
      FIFO)
   5. UMCON0 设置为 0x00 为不使用流控
   6. UBRDIV0 设置为 12 含义为 波特率设为 57600, 由下面公式算得:
        UBRDIVn = (int) (PCLK/bps*16) - 1
      其中 PCLK = 12MHz


发送/接收数据的代码说明如下:
   1. UTRSTA0 (UART TX/RX status register 0 )
      bit[1]:无数据发送时自动设为1,我们要用串口发送数据时,先读此位以判断是否有
      数据正在发送。
      bit[0]:接收缓冲区是否有数据,如果有,此位自动设为1,我们需要读此位来判断是
      否接收到了数据。
   2. UTXH0: 把要发送的数据写入此寄存器
   3. URXH0: 读此寄存器会得到串口接收到的数据


  
   /*
    * 测试代码 main.c
    * 作用:将从串口接收的数据发回串口
    */
   #include "serl.h"
  
   int main(void) {
     unsigned char ch;
     init_uart();
     while (1) {
       ch = getc();
       /* 如果接收到的是回车符就发送回车和换行符*/
       if (ch == 0x0d) {
         putc(0x0d);
         putc(0x0a);
       }
       else {
         putc(ch);
       }
     }
   }
    

   # 文件 Makefile
   # 由代码文件生成目标文件,并连接目标文件
   # 最后将连接生成的目标文件转换成二进制格式
   main:crt0.s serl.c main.c
        arm-linux-gcc -c -o crt0.o crt0.s
        arm-linux-gcc -c -o serl.o serl.c
        arm-linux-gcc -c -o main.o main.c
        arm-linux-ld  -Ttext 0x00000000 crt0.o serl.o main.o -o main_tmp.o
        arm-linux-objcopy -O binary -S main_tmp.o main
   clean:
        rm -f *.o
        rm -f main


三、编译、烧写、测试
   Make 一下就会生成我们要的文件 main, 将其通过 JTAG 烧入 Nand Flash。用超级终
连接到开发板,注意波特率设为 57600,数据位 8,无奇偶校正,停止位1,无数据流控制。现
在 Reset 一下的开发板,在超级终上输入一些字符,看到你自己输入的字符了吗?呵呵,再试
一试回车,超级终端上是不是换到了新的一行? 这就是简单的串口通信!     

posted on 2008-01-08 21:47 Normandy 阅读(7486) 评论(28)  编辑 收藏 引用 所属分类: Embeded Area

评论

# re: 学习 ARM 系列 -- FS2410 开发板上的串口通信编程  回复  更多评论   

好详细啊,支持支持!
2008-01-08 21:54 | hukq

# re: 学习 ARM 系列 -- FS2410 开发板上的串口通信编程  回复  更多评论   

不错的文章, 学习ing......
2008-01-09 09:51 | kyle

# re: 学习 ARM 系列 -- FS2410 开发板上的串口通信编程  回复  更多评论   

楼主太强了,简直就是我的偶像!
2008-01-14 19:09 | hukq

# re: 学习 ARM 系列 -- FS2410 开发板上的串口通信编程  回复  更多评论   

好东西。
if (ch == 0x0d) {
putc(0x0d);
putc(0x0a);
}
else {
putc(ch);
}
}
————————————
改成
if (ch == 0x0d)
putc(0x0a);
putc(ch);
是不是也可以?
2008-01-16 22:40 | qqwe

# re: 学习 ARM 系列 -- FS2410 开发板上的串口通信编程  回复  更多评论   

确实好啊,受教了。
2008-01-16 22:41 | free

# re: 学习 ARM 系列 -- FS2410 开发板上的串口通信编程  回复  更多评论   

@qqwe
可以这样精简一下:

putc(ch);
if (ch == 0x0d) putc(0x0a);

0x0d 代表回车
0x0a 代表换行
它们的顺序应该是先回车再换行
2008-01-17 16:26 | Normandy

# re: 学习 ARM 系列 -- FS2410 开发板上的串口通信编程  回复  更多评论   

niu bi niu bi niu bee!!
2008-01-24 17:19 | niube' son

# re: 学习 ARM 系列 -- FS2410 开发板上的串口通信编程  回复  更多评论   

如果要是传一句“hello,2008”,这怎么传啊,这里面有数字和标点符号.

void putc(unsigned char ch) {
while (!(UTRSTAT0 & TXD0_READY));
UTXH0 = ch;
}

unsigned char getc(){
while (! (UTRSTAT0 & RXD0_READY));
return URXH0;
}
这两个函数里都是char类型的,有关系吗?
2008-03-19 12:05 | 含着泪的微笑

# re: 学习 ARM 系列 -- FS2410 开发板上的串口通信编程  回复  更多评论   

@含着泪的微笑
这两个函数是如下使用的:
getc() 会从 UART0 读取一个符, 它不管你向 UART0 输入了什么, 只要有数据就读取, 没有数据就等待。

putc() 会向 UART0 输出一个字符, 上位机的软件(如超级终端) 可以显示它输出的是什么

我想你的问题是这样的:
上位机向 ARM 板子上的 UART0 发送了一个串: "hello,2008", ARM 该如何获取到这个串, 是这样吧?
可以这样解决:

char *buf = (char *)0x30010000;
char *p = buf;
char c = getc();
while (c != 0x0d) {
*p++ = c;
c = getc();
}
*p = '\0';

0x0d 指回车, 意思是当接收到回车时,停止读取操作。这时 "hello,2008" 已经存入缓冲区 buf 里, 你可以再对这个 buf 进行操作了。
2008-03-19 15:26 | Normandy

# re: 学习 ARM 系列 -- FS2410 开发板上的串口通信编程  回复  更多评论   


谢谢你的回复!!!

1
我还想问你一下,如果上位机不是通过在“超级终上输入一些字符”该怎么样呢??
比如上位机自动发送“hello,2008”而不是通过人的输入,那么下位机就不能通过
“c != 0x0d”来判断输入的结束,这时该怎样解决呢??
2我做作业碰到这样一个要求“接收PC发来的用户数据(变长),并保存到基地址在0x01000000的Flash中,Flash的型号为 AM29L160B。”
b"接收PC发来的用户数据(变长)"就不知道该怎样判断了,是不是我要定一个规则?比如PC发来的用户数据要定义一个结束标志,比如就用"0x0d"来表示数据的结尾,当下位机检测到这位时,就说明数据终止。但这样也有一个问题,就是我送的文本中有换行符,岂不是就把一个完整的文本分割了?
a.向Flash中写入数据,有什么步骤吗?

=====
真是不好意思,来麻烦您!
再一次致谢!
2008-03-20 10:29 | 含着泪的微笑

# re: 学习 ARM 系列 -- FS2410 开发板上的串口通信编程  回复  更多评论   

1

"GPHCON 的 GPH2、GPH3用控制接收数据寄存器 RXD0 和发送数据寄存器 TXD0"这两个寄存器在实际应用中是连线用的吗?我看有些实验手册中没有写用这两个寄存器,为何?
2
GPHUP |= 0x0c (上拉),上拉有什么作用?是不是往GPHDAT中的位写1,端口就是1,输出电流。我做作业“接收PC命令,实现开关门(GPIO0 1:开,0:关)”
在设置I/O时有这方面的疑惑,看处理器手册上也没看明白。

=======
麻烦您了!谢谢!
2008-03-20 11:39 | 含着泪的微笑

# re: 学习 ARM 系列 -- FS2410 开发板上的串口通信编程  回复  更多评论   

你好,在你的初使化中,

UCON0 = 0x05; /* Loopback mode*/
不知你为何在程序中设置成这个模式??

------------------

2008-03-20 12:00 | 含着泪的微笑

# re: 学习 ARM 系列 -- FS2410 开发板上的串口通信编程  回复  更多评论   

@含着泪的微笑
归纳了你的问题,做答如下:
1. 不一定非得用超级终端, 你也可以自己写程序。将 PC 的串口和 ARM 开发板的串口相连后,只要你的程序向 PC 串口发数据, ARM 就能通过连线获得数据。
2. 至于如何标识一个字串结束, 这是一个协议问题,可指定一个数,比如 0x55AA,当收到的两个连续字符是0x55AA时,则停止接收。
3. 可先将数据存到 SDRAM 缓冲区,再写到 Nand Flash。至于如何写 Nand Flash, 可参考 vivi 的源代码,里面有一个向 Nand Flash 写数据的方法。
2008-03-20 12:20 | Normandy

# re: 学习 ARM 系列 -- FS2410 开发板上的串口通信编程  回复  更多评论   

@含着泪的微笑
0x05 代表查询模式,实现起来最简单。
2008-03-20 12:25 | Normandy

# re: 学习 ARM 系列 -- FS2410 开发板上的串口通信编程  回复  更多评论   

@含着泪的微笑
1. RXD0 和 TXD0 你并看不到,能连线的只有串口。
2. 电阻上拉,我的理解是拉高电压,使电路更稳定。
3. 仔细阅读板子附带的手册吧,把这个例子在你的板子上先跑起来
2008-03-20 12:37 | Normandy

# re: 学习 ARM 系列 -- FS2410 开发板上的串口通信编程  回复  更多评论   

谢谢你啊!!
2008-03-20 20:22 | 含着泪的微笑

# re: 学习 ARM 系列 -- FS2410 开发板上的串口通信编程  回复  更多评论   

Normandy,你好,我这两天一直在弄串口,无论是用ADS还是ARM-LINUX-GCC编译的烧进NAND FLASH后超级终端或者DNW都没有反映。能否将你的这段程序源码发给我一份?谢谢。wangfan#gmail.com,#改为@。
2008-03-23 10:22 | FAN

# re: 学习 ARM 系列 -- FS2410 开发板上的串口通信编程  回复  更多评论   

@FAN
你好,已将代码发到你邮箱,有问题及时讨论。
2008-03-24 10:08 | Normandy

# re: 学习 ARM 系列 -- FS2410 开发板上的串口通信编程  回复  更多评论   

谢谢,你的代码是没问题的,我再看看我的是哪儿出问题了。
2008-03-24 12:25 | FAN

# re: 学习 ARM 系列 -- FS2410 开发板上的串口通信编程  回复  更多评论   

#define TXD0_READY 0x2
上句中0x2换为0x4在这个例子中也可以的吧?
2008-03-24 14:01 | FAN

# re: 学习 ARM 系列 -- FS2410 开发板上的串口通信编程  回复  更多评论   

顶上去
2008-03-24 14:29 | niube' son

# re: 学习 ARM 系列 -- FS2410 开发板上的串口通信编程  回复  更多评论   

@FAN
我没有试过 #define TXD0_READY 0x4 查了一下手册,上面这样描述, 对于 UTRSTA0[2]:

Set to 1 automatically when the transmit buffer register has
no valid data to transmit and the transmit shift register is
empty.
0 = Not empty
1 = Transmitter (transmit buffer & shifter register) empty

我觉得可以,你可以试试,别忘了把结果告诉我啊 :-)
2008-03-25 09:34 | Normandy

# re: 学习 ARM 系列 -- FS2410 开发板上的串口通信编程  回复  更多评论   

@Normandy
我试过,可以的。
2008-03-25 13:02 | FAN

# re: 学习 ARM 系列 -- FS2410 开发板上的串口通信编程  回复  更多评论   

请问, 你写过2410的DMA based uart driver吗? 我的DMA uart发数据没问题, 但从hyper terminal收数据的时候不能触发DMA0 interrupt, 非常疑惑, 不知道从何调起. 你知道吗? 谢谢

2008-08-06 21:33 | 严植

# re: 学习 ARM 系列 -- FS2410 开发板上的串口通信编程  回复  更多评论   

@严植
上面例子是基于查询而非 DMA ,不过应该不难。我想可能是以下的问题:
(1) 超级终端收发数据有问题(看看设置,如波特率等是否正确)
(2) 板子串口有问题(我的经常是这样, 多插几次试试)
(3) UART 设成 DMA 中断方式有误 (请仔细查看手册, 确认相关的控制寄存器是否设置正确)
2008-08-07 10:35 | Normandy

# re: 学习 ARM 系列 -- FS2410 开发板上的串口通信编程  回复  更多评论   

你写的这种是不像DOS那样没有发送接收的窗口的?
2009-05-03 09:17 | xiamenxw

# re: 学习 ARM 系列 -- FS2410 开发板上的串口通信编程  回复  更多评论   

大哥,

您好!

请问一下FS2410的UART口连接笔记本电脑,可以怎么连接?
2011-12-19 18:08 | 杨军

# re: 学习 ARM 系列 -- FS2410 开发板上的串口通信编程  回复  更多评论   

你好,ARM网关串口通信采集根节点数据,要求用c写,需要用到ARM的哪些东西吗,本人对ARM一无所知,网上没有查到这样的资料,单纯c接收串口程序有点,可是网关盒子是ARM的,应该怎么做,求大神指教,或者提供相关资料,不胜感激。
2014-09-05 11:15 | 王勇

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理