uart


一、UART原理及UART部件使用方法
1、UART原理
通用异步收发器UART,用来传输串行数据:
        发送数据时,CPU将并行数据写入UART,UART按照一定格式在TxD线上串行发出;
        接收数据时,UART检测到RxD线上的信号,将串行收集放到缓冲区中,CPU即可读取UART获得的这些数据。
UART最精简的连线只有3根电线,TxD用于发送,RxD用于接收,Gnd用于提供参考电平。TxD和RxD数据线以“位”为最小传输单位。
帧由具有完整意义的若干位组成,它包含开始位、数据位、校验位和停止位;发送数据之前,UART之间要约定好数据传输速率(波特率的倒数)、数据的传输格式(多少个数据位、是否使用校验位、奇校验还是偶校验、多少个停止位)。
 
数据传输流程如下:
1)通常数据线处于空闲状态(1状态)
2)当要发送数据时,UART改变TxD数据线的状态(0状态)并维持1位的时间,接收方检测到开始位后,再等待1.5位的时间开始一位一位地检测数据线的状态得到所传输的数据。
3)UART一帧中可能有5-8位的数据,发送方一位一位地改变数据线的状态将它们发出去,首先发送最低位
4)如果使用校验功能,UART发送完数据后,还要发送1个校验位。使用奇校验或者偶校验,即统计数据位连同校验位中,1的总数是奇数还是偶数
5)最后,发送停止位,数据线恢复到空闲状态(1状态),停止位长度有3种,1位、1.5位、2位
 
2、S3C2440 UART的特性
S3C2440的UART有3个独立通道,每个通道可以工作于中断模式或DMA模式,UART由波特率发生器、发送器、接收器、控制逻辑组成。
S3C2440 UART的FIFO深度为64,发送数据时,CPU先将数据写入发送FIFO中,然后UART会自动将FIFO中的数据复制到“发送移位器”中,发送移位器将数据一位一位发送到TxDn数据线上。接收数据时,“接收移位器”将RxDn数据线上的数据一位一位地接收进来,然后复制到接收FIFO中,CPU即可从中读取数据。

3、S3C2440 UART的使用
        对于S3C2440,使用UART之前,需要设置波特率、传输格式(多少个数据位、是否使用校验位、奇校验或偶校验、多少个停止位、是否使用流量控制)、选择所涉及的管脚为UART功能、选择UART通道的工作模式为中断模式或DMA模式。设置好之后,往相关寄存器写入数据即可发送,读取相关寄存器即可接收到数据。通过查询状态寄存器或设置中断来获知数据是否发送完毕、是否接收到数据。
1)UART通道管脚设为UART功能
        UART通道0中,GPH2、GPH3分别用于TxD0、RxD0,使用UART通道0时,先设置GPHCON寄存器将GPH2、GPH3引脚的功能设为TxD0、RxD0。
2)UBRDIVn寄存器:设置波特率
S3C2440 UART的时钟源有两种选择:PCLK、UEXTCLK、FCLK/n,其中n的值通过UCON0-UCON2联合设置
UBRDIVn = (int)(UART clock)/(buad rate × 16)) - 1
3)ULCONn寄存器:设置传输格式

4)UCONn寄存器
        它用于选择UART时钟源、设置UART中断方式

5)UFCONn寄存器、UFSTATn寄存器
        UFCONn寄存器用于设置是否使用FIFO,设置各FIFO的触发阙值,即发送FIFO中有多少个数据时产生中断、接收FIFO中有多少个数据时产生中断。并可以通过设置UFCONn寄存器来复位各个FIFO。
        读取UFSTATn寄存器可以知道各个FIFO是否已经满,其中有多少个数据。
6)UMCONn寄存器、UMSTATn寄存器
这两类寄存器用于流量控制,具体看数据手册
7)UTRSTATn寄存器
它用来表明数据是否已经发送完毕、是否已经接收到数据

8)UERSTATn寄存器
        用来表示各种错误是否发生

9)UTXHn寄存器
CPU将数据写入这个寄存器,UART即会将它保存到缓冲区中,并自动发送出去
10)URXHn寄存器
当UART接收到数据时,CPU读取这个寄存器,即可获得数据。
 
二、UART操作实例
本实例实现,在串口输入一个字符,开发板接收到后,从串口输出。
初始化代码和前一章类似,不在重复,在main函数中调用uart0_init()对串口进行初始化
 
//main.c
#include "serial.h"
int main()
{
    unsigned char c;
    uart0_init();   // 波特率115200,8N1(8个数据位,无校验位,1个停止位)
    while(1)
    {
        // 从串口接收数据后,判断其是否数字或字母,若是则输出
        c = getc();
        putc(c);
    }
    return 0;
}
 
//serial.c
#include "s3c24xx.h"
#include "serial.h"
#define TXD0READY   (1<<2)
#define RXD0READY   (1)
#define PCLK            50000000    // init.c中的clock_init函数设置PCLK为50MHz
#define UART_CLK        PCLK        //  UART0的时钟源设为PCLK
#define UART_BAUD_RATE  115200      // 波特率
#define UART_BRD        ((UART_CLK  / (UART_BAUD_RATE * 16)) - 1)
/*
 * 初始化UART0
 * 115200,8N1,无流控
 */
void uart0_init(void)
{
    GPHCON  |= 0xa0;    // GPH2,GPH3用作TXD0,RXD0
    GPHUP   = 0x0c;     // GPH2,GPH3内部上拉
    ULCON0  = 0x03;     // 8N1(8个数据位,无较验,1个停止位)
    UCON0   = 0x05;     // 查询方式,UART时钟源为PCLK
    UFCON0  = 0x00;     // 不使用FIFO
    UMCON0  = 0x00;     // 不使用流控
    UBRDIV0 = UART_BRD; // 波特率为115200
}
/*
 * 发送一个字符
 */
void putc(unsigned char c)
{
    /* 等待,直到发送缓冲区中的数据已经全部发送出去 */
    while (!(UTRSTAT0 & TXD0READY));
   
    /* 向UTXH0寄存器中写入数据,UART即自动将它发送出去 */
    UTXH0 = c;
}
/*
 * 接收字符
 */
unsigned char getc(void)
{
    /* 等待,直到接收缓冲区中的有数据 */
    while (!(UTRSTAT0 & RXD0READY));
   
    /* 直接读取URXH0寄存器,即可获得接收到的数据 */
    return URXH0;
}

 

1


Uart_Init 初始化UART012,设置相应的寄存器。设置波特率,数据位,奇偶校验位,FIFO等。具体的看datasheet


2

Uart_Select选择用哪个UART。函数里面的whichUart在后面的函数中有引用,决定用哪个UART

3

Uart_SendByte发送一个字符。

函数中 WrUTXH0('/r')   相当于 (*(volatile unsigned char *)0x50000020)=(unsigned char)('/r')   。而0x50000020UTXH0寄存器的地址。因为前面初始化串口的时候,FIFO disable 。 相当于 将 '/r' 发死赋值给UTXH0,然后UART发送 。UTXH0 UART channel 0 transmit buffer register                                                                                                                         rUTRSTAT0 & 0x2   取出rUTRSTAT0 寄存器中的第2位,含义为Transmit buffer是否为空 。为1时表示空。

while(!(rUTRSTAT0 & 0x2));   //Wait until THR is empty.注意这是一个空循环语句。后面有个分号,这代表空循环语句,直到THR为空时,就跳出循环,执行下一条。
  WrUTXH0(data);

4

Uart_Printf   在终端上显示字符串,这个不懂。这个跟SendString函数都可以在终端上显示字符。但Printf还可以像C语言那样,显示一些变量的值,使用%d,%s之类的

5

Uart_SendString   发送字符串。

6

Uart_Getch   从终端上获取敲入的字符。 返回值为char类型。RdURXH有数据时,返回URXH的数据。当URXH没有数据时,总是等待,直到有数据。这个函数,非要从终端那获得字符,否则一直等待。

7

Uart_GetKey 这个与Uart_Getch 不同的是,当URXH没有数据时返回0。有数据时,返回数据,这个函数用来查看URXH中的值。

8

Uart_GetString (char *string) 从终端那得到一个字符串,并存储到 字符型指针string 中。

void Uart_GetString(char *string)
{
    char *string2 = string;
------指针string2用来做比较

   char c;
    while((c = Uart_Getch())!='/r')
-----
输入的字符不是 回车,就存储到string
   {
        if(c=='/b')
-----
输入的是 退格(删除)
        {
            if( (int)string2 < (int)string ) ----sring
如果还没有输入数据(此时string2==string),就删除,不做任何操作。
            {
                Uart_Printf("/b /b");
------
实际中,我在调试的时候发现Uart_Printf("/b");并不能删除显示字符。Uart_Printf(" /b");也不能。Uart_Printf("/b /b/b /b");可以删除2个字符  
                string--; -----
有数据才删除
            }
        }
        else
        {
            *string++ = c;
-------
输入的 不是 回车和退格 ,将数据存储到string
            Uart_SendByte(c); ------
回写,没输入一个数据,在 超级终端上显示
       }
    }

    *string='/0'; -----
C语言中没有字符串变量,字符串不能存放在一个变量中,只能存放在字符型数组中。以'/0'作为字符串结束的标志。'/0'不计入串的长度。
    Uart_SendByte('/n'); ------
在超级终端上显示换行
}

9

Uart_GetIntNum         此函数是将转换为10进制,因为从UART从 超级终端 得到的10进制、或16进制,都是当做字符在处理。这个函数,就是将输入的如:"0X12BD""-0x334ACC""-BBH""123"之类的字符串,转换成10进制。由此可知,不支持输入二进制和八进制表示的数

int Uart_GetIntNum(void)
{
    char str[30];
     -------
定义字符型数组。
    char *string = str; ------
将数组的首地址,赋值给指针string。数组名就是首地址
    int base     = 10; ---------
10进制
    int minus    = 0; ---------
判断是否为负,为负是minus=1
    int result   = 0; ---------
最后转换的结果,因为从 超级终端 那得到的都是字符

    int lastIndex;    --------
数组的最后一个字符是什么

    int i;
   
    Uart_GetString(string); ------
从超级终端那得到字符,并存储到string所指向的数组str[]中。
    
   if(string[0]=='-') -------
如果是负数
    {
        minus = 1;
------
更新负数标志
        string++; ------string
指向str[1]
    }
   
    if(string[0]=='0' && (string[1]=='x' || string[1]=='X')) -----
除去那个负号外,或没有负号,如果是以0x0X开头,即16进制

    {
        base    = 16;
-----
指示为16进制
        string += 2; -----
指向str[2],因为str[0]存储了字符'0'str[1]存储了字符'x''X'
   }
   
    lastIndex = strlen(string) - 1; -------strlen
函数,求字符串的长度。减1是因为,数组下标从0开始。'/0'不计入串的长度。

    
    if(lastIndex<0) ------
说明0x0X- 后面没有任何内容
        return -1;
   
    if(string[lastIndex]=='h' || string[lastIndex]=='H' ) ----
如果16进制是用Hh表示的
   {
        base = 16;
-------
指示是16进制的数
        string[lastIndex] = 0; ------
Hh去掉
        lastIndex--; -------
数组最后一位不要,即存储Hh的那位
   }

    if(base==10) -----如果是10进制
    {
        result = atoi(string);
----atoi
函数,将字符串转换成整型,转换后赋值给result
        result = minus ? (-1*result):result; ------
是负数,就在 乘以
-1
   }
    else -----
如果是16进制
    {
        for(i=0;i<=lastIndex;i++)
        {
            if(isalpha(string[i]))
-------isalpha
函数,判断是否是字母。
            {
                if(isupper(string[i]))
------isupper
函数,判断是否是大写字母
                    result = (result<<4) + string[i] - 'A' + 10; -------
将大写字母转换为相应的10进制
                else
                    result = (result<<4) + string[i] - 'a' + 10;
-------
将小写字母转换为相应的10进制
            } ------result<<4
,左移4位相当于乘以16,举例说明更好理解。如果字符串为ABCH,处理Aresult=0X16+0+10,处理Bresult=10(A)X16+11,处理C result=10(A)X16X16+11(B)X16,处理Cresult=10(A)X16X16X16+11(B)X16X16+12 。这样才能将16进制转化为正确的10进制

            else ------如果是数字字符,转换成十进制数,分析过程同上
                result = (result<<4) + string[i] - '0';
        }
        result = minus ? (-1*result):result;
----------
是负数,别忘了乘以负号
    }
    return result;
---------
将正确的10进制返回
}

10

Uart_GetIntNum_GJ   接受10进制的数,将'0''9'的字符,转换成10进制数。

11

Uart_TxEmpty   如果UART012transmit shifter registerbuffer register,一直是空的,就一直等待,直到不为空,就跳出循环

 

你可能感兴趣的:(ARM,string,终端,存储,buffer,c,语言)