TQ2440开发板学习纪实--- 设置UART串口,输出Hello World!

Posted smstong

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TQ2440开发板学习纪实--- 设置UART串口,输出Hello World!相关的知识,希望对你有一定的参考价值。

0 串口,UART,RS232,RS485傻傻分不清

0.1 串行通信与并行通信

串口名字表示采用的通信方式为串行而不是并行。那么串行与并行的区别是啥呢?很简单,串行就是同一时刻只能传输一个bit,而并行则可以同时传输多个bits。乍一看,仿佛并行通信速度更快,而且并行的信号线越多,传输数据快。CPU的总线就是使用并行通信的,总线带宽也从8一路飙升到了目前的64。然而这是在CPU的内部,对于设备间通信而言,并行通信意味着需要更多的信号连线,更复杂的控制逻辑,设备成本飙升。所以目前外围设备通信中,串行通信才是主流。如USB就是串行通信的代表,IDE的硬盘接口也被串行的SATA取代。
串口,顾名思义,采用的串行通信,一次发送一个bit位,干净利索。

0.2 异步通信和同步通信

UART是Universal Asynchronous Receiver&Transmitter的缩写,意思是统一异步通信。这里的关键点是异步通信。那就说明还存在另一种通信方式,叫做“同步通信”。这两者到底啥区别呢?
具体看参考这篇文章。总结来说:

  1. 同步通信要求接收端时钟频率和发送端时钟频率一致,发送端发送连续的比特流;异步通信时不要求接收端时钟和发送端时钟同步,发送端发送完一个字节后,可经过任意长的时间间隔再发送下一个字节。
  2. 同步通信效率高;异步通信效率较低。
  3. 同步通信较复杂,双方时钟的允许误差较小;异步通信简单,双方时钟可允许一定误差。
  4. 同步通信可用于点对多点;异步通信只适用于点对点。

0.3 RS232 RS485

串口通信有自己工业标准,目前常用的标准有两种:一是RS232,二是RS485。他们并不兼容,各自规定了自己的电气标准,通信规范。参考这篇文章
简单来说:
由于RS232接口标准出现较早,难免有不足之处,主要有以下四点:
  (1) 接口的信号电平值较高,易损坏接口电路的芯片,又因为与TTL 电平不兼容故需使用电平转换电路方能与TTL电路连接。
  (2) 传输速率较低,在异步传输时,波特率为20Kbps。
  (3) 接口使用一根信号线和一根信号返回线而构成共地的传输形式,容易产生共模干扰,所以抗噪声干扰性弱。
  (4) 传输距离有限,最大传输距离标准值为50英尺,实际上也只能用在50米左右。
 针对RS232接口的不足,于是就不断出现了一些新的接口标准,RS-485就是其中之一,它具有以下特点:
  (1) RS-485的电气特性:逻辑“1”以两线间的电压差为+(2-6) V表示;逻辑“0”以两线间的电压差为-(2-6)V表示。接口信号电平比RS-232降低了,就不易损坏接口电路的芯片,且该电平与TTL电平兼容,可方便与TTL 电路连接。
  (2) RS-485的数据最高传输速率为10Mbps 。
  (3) RS-485接口是采用平衡驱动器和差分接收器的组合,抗共模干能力增强,即抗噪声干扰性好。
  (4) RS-485接口的最大传输距离标准值为4000英尺,实际上可达3000米,另外RS-232接口在总线上只允许连接1个收发器,即单站能力。而RS-485接口在总线上是允许连接多达128个收发器。即具有多站能力,这样用户可以利用单一的RS-485接口方便地建立起设备网络。

1 S3C2440片内串口控制器初始化理论

S3C2440片内集成了UART控制器,支持3个独立的串口。TQ2440开发板对其中的0号串口引脚连接了电平转换电路把TTL电平转换成了RS232标准的电平,并搭配了标准的DB9接头。这样就可以直接和PC的DB9接口通过串口线连接调试了。

1.1 时钟来源与波特率

串口通信需要时钟来驱动,S3C2440提供了三种时钟来源供UART控制器使用,分别是PCLK,UEXTCLK,FCLK/n。具体使用哪个时钟源通过UCONn寄存器来设置。
波特率以时钟源的频率为基础,按照固定的公式计算得出。

UBRDIVn = (时钟源频率 / (波特率X16) -1

设置波特率的寄存器为UBRDIVn。
我们的实验中,选择PCLK作为时钟源,频率为50MHz,如果要想得到115200Hz的波特率,那么就需要设置

UBRDIVn = 50,000,000 / 115200*16 -1 = 26

1.2 数据长度、停止位、校验位

这是串口通信的三要素,收发两端必须一致才能正确通信。通过ULCONn寄存器来设置。详见数据手册。

1.3 轮询方式发送一个字节的流程

需要发送数据时
(1)把数据写入UTXHn寄存器,写完成导致:
* 发送完成状态清0;
* UART硬件自动把其中的数据串行化发送出去;
* 发送完毕UTRSTATn的发送完成状态置1。

(2)循环检测发送完毕状态位,等待发送完毕退出。

1.4 轮询方式接收数据流程

接收数据也很简单
当接收到数据时,会把数据反串行化后放入URXHn寄存器中,并设置URTSTATn寄存器来通知。
(1)循环检测UTRSTATn的接收完成状态,直到其为1
(2)读取URXHn寄存器,取出收到的字节,读完成会导致:

  • UTRSTATn的接收完成状态清0

2 源码

uart.c

#include "2440addr.h"

void uart_init(void)

    rGPHCON &= ~(3<<4 | 3<<6);
    rGPHCON |= 2<<4 | 2<<6;
    rGPHUP = 0x00;
    rULCON0 &= ~(0x3);  /* 8 bits */
    rULCON0 |= 0x3;
    rULCON0 &= ~(1<<2); /* 1 stopbit */
    rULCON0 &= ~(1<<5); /* no parity */
    rULCON0 &= ~(1<<6); /* normal (not infrared) */

    rUCON0 &= ~(3);     /* open send mode */
    rUCON0 |= 0x1;
    rUCON0 &= ~(3<<2);  /* open recv mode */
    rUCON0 |= 1<<2;

    rUCON0 &= ~(3<<10); /* PCLK */

    rUBRDIV0 = 26;      /* baud rate = 115200 */

void uart_set_baud(int baud)

    if (baud == 115200 ) 
        rUBRDIV0 = 26;
     else if(baud == 9600 ) 
        rUBRDIV0 = 325;
     else 
    

void uart_set_stopbits(int n)

    if (n < 1 || n > 2) return;
    if (n == 1) 
        rULCON0 &= ~(1<<2);
    else
        rULCON0 |= 0x4;
    

/* 0 - no, 1 - odd, 2 - even */
void uart_set_parity(int how)

    if (how < 0 || how >2) return;
    rULCON0 &= ~(0x7 << 3);
    if (how == 0) 
        rULCON0 |= (0 << 3);
     else if (how == 1) 
        rULCON0 |= (4 << 3);
     else 
        rULCON0 |= (5 << 3);
    

/* n=5,6,7,8 */
void uart_set_databits(int n)

    if (n<5 || n>8) return;
    rULCON0 &= ~3;
    switch (n) 
        case 5:
            rULCON0 |= 0;
            break;
        case 6:
            rULCON0 |= 1;
            break;
        case 7:
            rULCON0 |= 2;
            break;
        case 8:
            rULCON0 |= 3;
            break;
        default:
            break;
    

/* send a byte and wait until sent. */
void uart_send_byte(unsigned char c)

    rUTXH0 = c;
    while ( 0 == (rUTRSTAT0 & (1<<2)) );

unsigned char uart_recv_byte()

    while ( 0 == (rUTRSTAT0 & 1)) ;
    return rURXH0;


void uart_send_str(char* msg)

    while (*msg) 
        uart_send_byte(*msg++);
    

3 简单的打印函数

这里我们原样模拟了标准C库的函数。

3.1 putchar

注意对C字符串换行符的处理,需要转换成终端换行符序列0x0A0D。

int putchar(int c)

    if(c == '\\n') 
        uart_send_byte(0x0A);
        uart_send_byte(0x0D);
     else 
        uart_send_byte((unsigned char)c);
    
    return 0;


3.2 puts

int puts(const char* str)

    while(*str)
        putchar(*str++);
    
    return 0;

3.3 测试代码

 io_init(0);
 puts("Hello, World!\\n");

4 项目源码

标签v0.6。

以上是关于TQ2440开发板学习纪实--- 设置UART串口,输出Hello World!的主要内容,如果未能解决你的问题,请参考以下文章

TQ2440开发板学习纪实--- 设置堆栈并调用C函数

TQ2440开发板学习纪实--- 设置时钟频率,让CPU运行的更快

TQ2440开发板学习纪实--- 启用IRQ中断,告别低效的轮询!

TQ2440开发板学习纪实--- 设置时钟频率,让CPU运行的更快

TQ2440开发板学习纪实--- 利用Undefined异常模拟BLX指令

TQ2440开发板学习纪实(10)--- 实现多任务处理,最简单OS模型